※ 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 |