spring security 기본 예제

뿌이·2022년 1월 20일
0

spring

목록 보기
5/16


페이지 별로 접근가능 권한을 확인하는 예제를 해보려고 한다

SampleController.java

package org.conan.controller;

import java.util.ArrayList;
import java.util.Arrays;

import org.conan.domain.SampleDTO;
import org.conan.domain.SampleDTOList;
import org.conan.domain.TodoDTO;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import lombok.extern.log4j.Log4j;

@Controller								// 컨트롤러라는 걸 알리기 위한 선언 
@Log4j									// 콘솔창에 log로 찍기 위해 선언하는 것
@RequestMapping("/sample/*")			// 주소창에 /sample로 들어오는 건 다 처리
public class SampleController {
	
//	@InitBinder
//	public void initBinder(WebDataBinder binder) {
//		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
//		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(dateFormat,false));
//	}
	
	@GetMapping("/ex01")
	public String ex01(SampleDTO dto) {
		log.info("" + dto);				// 콘솔창에 띄우기 위한 출력문
		return "ex01";
	}
	
	@GetMapping("/ex02")
	public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
			log.info("name : " + name);
			log.info("age : "+ age);
		return "ex02";
	}
	
	@GetMapping("/ex02List")
	public String ex02List(@RequestParam("ids") ArrayList<String> ids) {
			log.info("ids : " + ids);
		return "ex02List";
	}
	
	@GetMapping("/ex02Array")
	public String ex02Array(@RequestParam("ids") String[] ids) {
			log.info("array ids : " + Arrays.toString(ids));
		return "ex02Array";
	}
	
	@GetMapping("/ex02Bean")
	public String ex02Bean(SampleDTOList list) {
			log.info("list dtos : " + list);
		return "ex02Bean";
	}
	
	@GetMapping("/ex03")
	public String ex03(TodoDTO todo) {
			log.info("todo : " + todo);
		return "ex03";
	}
	
	@GetMapping("/ex04")
	public String ex04(SampleDTO dto, @ModelAttribute("page")int page) {
										// 강제로 전달받은 파라미터를 모델에 담아서 타입에 관계없이 전달
			log.info("dto : " + dto);
			log.info("age : " + page);
		return "/sample/ex04";
	}
	
	@GetMapping("/ex05")
	public void ex05() {
		log.info("ex05........................");
	}
	
	@GetMapping("/ex06")
	public @ResponseBody SampleDTO ex06() { 
		log.info("ex06........................");
		SampleDTO dto = new SampleDTO();
		dto.setAge(10);
		dto.setName("conan");
		return dto;
	}
	
	@GetMapping("/ex07")
	public ResponseEntity <String> ex07(){
		log.info("ex07....");
		String msg = String.format("{\"name\":\"conan\"}");
		HttpHeaders header = new HttpHeaders();
		header.add("Content-Type", "application/json;charset=UTF-8");
		return new ResponseEntity<>(msg,header,HttpStatus.OK);
	}
	
	@GetMapping("/exUpload")
	public void exUpload() {
		log.info("exUpload......");
	}
	
	@PostMapping("/exUploadPost")
	public void exUploadPost(ArrayList<MultipartFile> files) {
		for(MultipartFile file:files) {
			log.info("name : " + file.getOriginalFilename());
			log.info("size : " + file.getSize());
		}
	}
	
	@GetMapping("/all")
	public void doAll() {
		log.info("누구나 접근 가능");
	}
	
	@GetMapping("/member")
	public void doMember() {
		log.info("로그인 한 회원들만 접근 가능");
	}
	
	@GetMapping("/admin")
	public void doAdmin() {
		log.info("관리자들만 접근 가능");
	}
}


jsp만들고 페이지 이동 확인후에

security-context.xml에 추가하기

<security:http>
	
		<security:intercept-url pattern="/sample/all" access="permitAll"/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		
		<security:form-login />
	</security:http>
	<security:authentication-manager></security:authentication-manager>

이걸 추가하면 /sample/member로 접근시에
자동으로 spring security 에서 만들어준 로그인화면이제공된다
근데 난안된다..

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_4_0.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/security-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		
		<!-- 파일 업로드 -->
		<multipart-config>
			<location>c:\\upload</location> <!-- 업로드되는 파일 저장 공간 -->
			<max-file-size>20971520</max-file-size> <!-- 업로드되는 파일의 최대 크기 -->
			<max-request-size>41943040</max-request-size> <!-- 한 번에 올릴 수 있는 최대 크기 -->
			<file-size-threshold>20971520</file-size-threshold> <!-- 특정 사이즈의 메모리 사용 -->
		</multipart-config>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	
	<filter>
		<filter-name>encoding</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	
	<filter-mapping>
		<filter-name>encoding</filter-name>
		<servlet-name>appServlet</servlet-name>
	</filter-mapping>
	
	<!-- spring security -->
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
			<filter-name>springSecurityFilterChain</filter-name>
			<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

security를 읽는걸 추가해놔야 security-context.xml을 읽을 수 있음

되는걸로 받아서 security-context.xml에 좀 더 추가함
그러면

security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:security="http://www.springframework.org/schema/security"
   xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-5.2.xsd
      http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">


   <security:http>
   <security:intercept-url pattern="/sample/all" access="permitAll" />
   <security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')" />
      <security:form-login/>
   </security:http>
   <security:authentication-manager>
   <security:authentication-provider>
   	<security:user-service>
   		<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
   	</security:user-service>
   </security:authentication-provider>
   </security:authentication-manager>

</beans>

member, member로만 로그인 가능

{noop}없으면 null값으로 들어감 (이게 encoding 해주는 기능임)
임시기능이라고 보면됨

그래서 member로 로그인 한 후 admin페이지에 접근하려고 하면


이렇게 나와야한당

그리고 이제 admin을 추가해줄것임\

security-context.xml 전체코드

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:security="http://www.springframework.org/schema/security"
   xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-5.2.xsd
      http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">


   <security:http>
   <security:intercept-url pattern="/sample/all" access="permitAll" />
   <security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')" />
   <security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')" />
      <security:form-login/>
   </security:http>
   <security:authentication-manager>
   <security:authentication-provider>
   	<security:user-service>
   		<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
   		<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
   	</security:user-service>
   </security:authentication-provider>
   </security:authentication-manager>

</beans>

이렇게 하면 admin은 member 페이지도 접근할 수 있지만
member는 admin페이지에 접근불가함

error페이지 제작하기

CommonController.java

CommonController를 만든다

package org.conan.controller;

import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.log4j.Log4j;

@Controller
@Log4j
public class CommonController {
	@GetMapping("/accessError")
	public void accessDenied(Authentication auth, Model model) {
		log.info("access Denied : "+auth);
		model.addAttribute("msg", "Access Denied");
	}
}

그다음

accessError.jsp를 만든다

accessError.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Access Denied Page</h1>
<h2><c:out value="${SPRING_SECURITY_403_EXCEPTION.getMessage() }"/></h2>
<h2><c:out value="${msg }"/></h2>
</body>
</html>

security-context.xml

에다가 코드 추가
전체코드임

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:security="http://www.springframework.org/schema/security"
   xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-5.2.xsd
      http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">


   <security:http auto-config="true" use-expressions="true">
   <security:intercept-url pattern="/sample/all" access="permitAll" />
   <security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')" />
   <security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')" />
      <security:form-login/>
   <security:access-denied-handler error-page="/accessError"/>
   </security:http>
   <security:authentication-manager>
   <security:authentication-provider>
   	<security:user-service>
   		<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
   		<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
   	</security:user-service>
   </security:authentication-provider>
   </security:authentication-manager>

</beans>

에러가 나면 context에서 먼저 읽고 컨트롤러로 보내줘서
컨트롤러에서 우리가 만든 에러페이지로 보내주는 것임


다른 에러페이지 만들기

그다음

만들어준다

CustomAccessDeniedHandler.java

package org.conan.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import lombok.extern.log4j.Log4j;

@Log4j
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException, ServletException{
		log.error("Access Denied Handler");
		log.error("Redirect");
		response.sendRedirect("/accessError");
	}
}

security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:security="http://www.springframework.org/schema/security"
 xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-5.2.xsd
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">


 <security:http auto-config="true" use-expressions="true">
 <security:intercept-url pattern="/sample/all" access="permitAll" />
 <security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')" />
 <security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')" />
    <security:form-login login-page="/customLogin"/>
    <!-- 내가 만든 로그인 페이지(/customLogin) 를 쓰겠다고 하는거 -->
 <security:access-denied-handler ref="customAccessDenied"/>
 </security:http>
 <security:authentication-manager>
 <security:authentication-provider>
 	<security:user-service>
 		<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
 		<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
 	</security:user-service>
 </security:authentication-provider>
 </security:authentication-manager>
		
<bean id="customAccessDenied" class="org.conan.security.CustomAccessDeniedHandler"></bean>
</beans>

내가 만든 로그인 페이지로 들어가게 하기

CustomAccessDeniedHandler.java

package org.conan.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import lombok.extern.log4j.Log4j;

@Log4j
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException, ServletException{
		log.error("Access Denied Handler");
		log.error("Redirect");
		response.sendRedirect("/accessError");
	}
}

CommonController.java

package org.conan.controller;

import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.log4j.Log4j;

@Controller
@Log4j
public class CommonController {
	@GetMapping("/accessError")
	public void accessDenied(Authentication auth, Model model) {
		log.info("access Denied : "+auth);
		model.addAttribute("msg", "Access Denied");
	}
	
	@GetMapping("/customLogin")
	public void loginInput(String error, String logout, Model model) {
		log.info("error : "+error);
		log.info("logout : "+error);
		if(error != null) {
			model.addAttribute("error", "Login Error Check Your Account");
		}
		if(logout != null) {
			model.addAttribute("logout", "LogOut!");
		}
		
	}
}

Login페이지에서 csrf토큰이 생성됐는지 확인해보기


검사기 켜서 확인후 응용프로그램-쿠키 지우고 다시 확인

csrf의 값이 다른것을 확인할 수 있음
매우잘되고있음

profile
기록이 쌓이면 지식이 된다.

0개의 댓글