Spring/Spring

23-04-27 Spring (Spring Web Security)

모건이삼촌 2023. 4. 27. 14:55

※ Spring Web Security

 

1. Filter

 - 인코딩 변환처리(utf-8등), XSS방어 등의 요청에 대한 처리로 사용됨

 - 톰캣과 관계되어 있음

 

2. interceptor (WebSocket 할 때 사용)

 - 자바 단계가 아닌 자바 전 단계에서 실행됨(advice 등)

 - 스프링과 가장 무관함

 - 내부에서 컨트롤러에 관한 요청과 응답에 대해 처리함

 - 스프링의 Bean에 접근할 수 있으며 인터셉터 조차도 Bean으로 등록하여 사용해야함

 - 인터셉터는 여러개 사용할 수 있고, 로그인체크, 권한체크, 프로그램 실행시간 계산작업 로그확인등의 업무처리에 사용

 - 요청에 대한 작업 전/후로 가로챈다고 보면 된다.

 - 인터셉터는 채팅을 구현할 때 사용했음. 

 - request, response가 필수적임 

 

 

3. AOP

 - 프록시 패턴의 형태로 실행됨

 - 로깅, 트랜잭션(주로 사용), 에러처리 등 비즈니스등 메서드에서 조금 더 세밀하게 조정하고 싶을 때 사용함

 

4. 실행순서

 - filter는 가장 바깥, aop는 메소드 앞 프록시 패턴의 형태로 실행됨

 - Filter => Dispatcher Servlet => Interceptor => AOP => Controller

 

 

Authentication (인증)\

Authorization (인가/권한)

 

1-1. pom.xml dependency 추가

		<!-- Security -->
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-core</artifactId>
		    <version>${org.springframework-version}</version>
		</dependency>
		
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-web</artifactId>
		    <version>${org.springframework-version}</version>
		</dependency>
		
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-config</artifactId>
		    <version>${org.springframework-version}</version>
		</dependency>
		
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-taglibs</artifactId>
		    <version>${org.springframework-version}</version>
		</dependency>

버전은 현재 스프링버전으로 변경한다.

 

1-2. WebConfig 필터에 구문 추가

	@Override // 한글깨짐처리
	protected Filter[] getServletFilters() {
		// 04-27
		// security Filter 추가 
		DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy(); 
		CharacterEncodingFilter filter = new CharacterEncodingFilter("utf-8", true);
		return new Filter[] {filter, delegatingFilterProxy};
	}

1-3. src/main/java -> config 패키지에 클래스 추가

package com.chanyongyang.config;

public class SecurityConfig {

}

만들어만 두면 된다.

 

1-4. WebConfig getRootConfigClasses 구문 추가

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] {RootConfig.class, SecurityConfig.class};
	}

로딩설정, security에 대한 최소설정이다.

 

 

1-5 Security클래스 구문 추가

package com.chanyongyang.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SecurityConfig {
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
		return http.build();
	}
}

현재 실행안됨

 

 

 

※ 다른방법

 

security라는 레거시 프로젝트 생성

xml설정했던 프로젝트의 pom.xml 복붙 후 오늘 추가했던 security dependency 추가하면 된다.

 

rootcontext위치에 security-context.xml 추가

주의사항 Spring Bean Configuration으로 만들어야함.

<?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 http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:http>
		<security:form-login/>
	</security:http>
	
	<security:authentication-manager></security:authentication-manager>
</beans>

web.xml 3.1버전으로 변경 및 필터 추가, 컨텍스트 파람에 시큐리티콘텍스트 경로 추가

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	version="3.1">
	
	<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>

	<!-- 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 Conta

 

2. sampleController 생성

package com.chanyongyang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.log4j.Log4j;

@Controller
@Log4j
@RequestMapping("sample/*")
public class SampleController {
	
	@GetMapping("all")
	public void doAll() {
		log.info("doAll()");
	}
	
	@GetMapping("member")
	public void doMember() {
		log.info("doMember()");
	}
	
	@GetMapping("admin")
	public void doAdmin() {
		log.info("doAdmin()");
	}
}

위 컨트롤러 mapping한 값에 맞는 sample/??.jsp 생성 => 서버 구동후 확인

 

3. security-context 구문 추가

<?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 http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:http>
		<security:form-login/>
        // 추가된 코드
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
	</security:http>
	
	<security:authentication-manager></security:authentication-manager>
</beans>

이후 서버를 재시작하고 member로 이동하게 되면 로그인창이 생성된다.

(룰을 멤버라고 설정했기 때문에 로그인창이 뜨게되는것 실제로 로그인 해야 원하는창이 나오게됨)

 

4. 계정생성 및 로그인 하기

<?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 http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:http>
		<security:form-login/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
	</security:http>
	
	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="member" password="member" authorities="ROLE_MEMBER"/> <!-- 위에 hasRole과 일치해야함 -->
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
</beans>

security:user의 authorities에는 위에 security:intercept-url에서 사용된 access의 hasRole과 동일해야한다.

내용 : 패스워드 인코더가 없음

해결방법 : 패스워드인코더 의 bean을 등록하면 됨

 

<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>

noop은 no 오퍼레이션(연산작업을 하지 않겠다는 뜻)

 

 

하고 위에 셋팅했던 아이디와 비밀번호를 입력하면 아래 화면이 출력된다.(세션이 등록되었다는 뜻임)

 

5. admin(여러 권한을 가지는 사용자 설정)

<?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 http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:http>
		<security:form-login/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/>
		
		<!-- 로그아웃 -->
		<security:logout  logout-url="/sample/logout" logout-success-url="/sample/all"/>
		<security:csrf disabled="true"/>
	</security:http>
	
	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/> <!-- 위에 hasRole과 일치해야함 -->
				<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/> <!-- 위에 hasRole과 일치해야함 -->
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
</beans>

 - member가 admin페이지에 접근하면 접근이 금지된다는 403에러가 뜨게된다.

 - admin이 member의 권한도 갖게 하려면 security:user구문에 authorities에 ',' 구분자를 사용하여 member도 추가해주면 된다.

 - 로그아웃도 시큐리티에 등록하고 로그아웃이 필요한 해당 페이지에 폼태그를 이용하여 간단하게 로그아웃버튼을 만들어 formaction을 보내면 된다.

 

6. 접근제한 메세지의 처리

예를들어 멤버권한을 가진 사람이 관리자 권한을 요구하는 페이지에 넘어갈경우(403에러) 그 페이지를 처리하는 방법

 

6-1. commonController 생성

package com.chanyongyang.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 accessError(Authentication auth, Model model) {
		log.info("access denied");
		model.addAttribute("auth", auth);
	}
}

getMapping을 해줬으니 그 이름에 맞는 jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>2023. 4. 27.오후 12:04:19</title>
</head>
<body>
	<h1>Access Denied</h1>
	<p>${auth}</p>
</body>
</html>

security-context의 security:http에 구문 추가

		<!-- 에러페이지 설정 -->
		<security:access-denied-handler error-page="/accessError"/>

 

그 후 실행하여 member권한을 가진 유저로 로그인 한 뒤 admin으로 넘어가면 이런 페이지가 나온다.

 

7. 커스텀 로그인페이지 

 - 로그인 생김새가 너무 구려 로그인 페이지를 새로 생성하려함

7-1. memberController 생성

package com.chanyongyang.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("member")
public class MemberController {
	
	@GetMapping("login")
	public void login() {
		
	}
	@PostMapping("login")
	public void login(HttpServletRequest req) {
		
	}
}

member/login.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>2023. 4. 27.오후 12:18:35</title>
</head>
<body>
	<form method="post" action="/login">
		<input type="text" name="username">
		<input type="password" name="password">
		<button>로그인</button>
	</form>
</body>
</html>

security-context 설정

	<security:http>
		<!-- 일반적인 시큐리티 설정 -->
		<security:form-login login-page="/member/login"/>

 

admin이나 member 이동시 커스텀로그인창이 나온다.

 

8. csrf

<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
<table>
	<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
	<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
	<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
	<input name="_csrf" type="hidden" value="703c70f9-743e-4fe7-a9d2-452e9dc2d5fe" />
</table>
</form></body></html>

위 소스는 커스텀로그인을 해제하고 기존에 있던 기본 로그인폼의 소스이다.

input type hidden에는 쿠키마다 설정되는 각자의 고유값이 있다.

이 고유값을 확인하여 csrf공격을 막을 수 있다.

 

1. security-context

<?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 http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:http>
		<!-- 일반적인 시큐리티 설정 -->
		<security:form-login login-page="/member/login"/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/>
		
		<!-- 로그아웃 -->
		<security:logout  logout-url="/sample/logout" logout-success-url="/sample/all"/>
		<!-- <security:csrf disabled="true"/> -->
		
		<!-- 에러페이지 설정 -->
		<security:access-denied-handler error-page="/accessError"/>
		
		<!-- 자동로그인 / false로 설정하면 자동로그인 해제 -->
		<!-- <security:remember-me/> -->
	</security:http>
	
	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/> <!-- 위에 hasRole과 일치해야함 -->
				<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/> <!-- admin과 member 두개의 권한을 줌 -->
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
</beans>

login.jsp, admin.jsp(member.jsp도 같음), accessError.jsp form 태그안에 구문 추가

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">

'Spring > Spring' 카테고리의 다른 글

23-03-29 Spring  (0) 2023.03.30
23-03-23 Spring  (0) 2023.03.23
23-03-22 Spring  (0) 2023.03.22
23-03-21 Spring 비밀글  (0) 2023.03.21
23-03-17 Spring  (0) 2023.03.17