스프링 시큐리티는 스프링 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크
username : 유저ID
series : 동일한 유저일 경우 브라우저별로 구분할 고유 값
크롬과 익스플로러에서 동일한 유저가 두 브라우저에서 자동로그인을 체크하고 로그인하면,
두 개의 row가 insert 됨. 그래서, 이런 경우의 구분을 위해서 고유한 series값을 사용함
이렇게 신규 발급이 된 이후부터는 series 값으로 사용자 정보를 검색
⇒ token 값을 새로 발행 ⇒ persistent_logins taable에서 series 값으로 검색한 후,
새로 발행된 token 값으로 update
token : 브라우저가 가지고 있는 쿠키의 값을 검증할 인증값
다시 브라우저에서 접속을 하면, 자동 로그인이 설정이 되었는지 확인
remember-me 쿠키(series:token)값을 복호화
series 값으로 token 값을 검색 → 동일하다면, 새로운 token 값을 발행해서 table에 update함
브라우저의 remember-me 쿠키 값을 갱신함
last_used : 마지막으로 로그인한 시간정보
security:remember-me의 token-validity-seconds 속성 관련
token-validity-seconds + last_used : 유효시간
로그인 시 현재 서버의 시간보다 유효시간이 이전이면→ 유효. 자동로그인 됨
이후이면→ 무효. 자동로그인 안됨
기본 동작 방식 : 서블릿의 여러 종류의 필터 + 인터셉터를 이용한 처리
스프링 시큐리티를 알기 위해서는 서블릿 필터로 개발한 시큐리티 필터에 대한 이해가 필요하다.
서블릿 필터는 클라이언트의 요청을 가로채서 서블릿이 수행되기 전후에 전처리와 후처리를 수행하거나 요청을 리다이렉트하는 용도로 사용한다.
일반적으로 필터 한 개당 하나의 기능을 처리하기 때문에 여러 기능이 필요한 경우에는 여러 개의 필터를 만들어 필터 체인을 형성하여 사용한다.
스프링 시큐리티는 수많은 필터를 제공하는데, 각 필터들이 시큐리티 처리를 위한 기능을 제공하며 상호작용한다.
스프링 시큐리티를 구성하는 필터 중 가장 중요한 필터
사용자가 입력한 인증 정보를 이용하여 실제로 인증을 처리하는 필터이다.
인증에 성공한 사용자가 해당 리소스에 접근할 권한이 있는지를 검증한다.
사용자의 리소스 요청
인증 관리 필터가 사용자 요청을 가로챔
2.1 인증 처리 관련 필터를 사용하여 사용자 인증을 처리하기 시작
2.2 인증되지 않은 사용자의 접근 시 인증에 필요한 정보를 요청
2.3 사용자의 인증 정보 입력
2.4 파일 또는 DB에서 사용자 정보를 읽어 인증 정보를 검증
인증 통과 시 사용자가 요청한 리소스로 요청 전달
권한 관리 필터의 요청 가로채기
4.1 리소스에 대한 접근 권한 검증
4.2 리소스별 권한 목록 참조하여 접근 권한 검증한다.
필터와 인터셉터는 특정한 서블릿이나 컨트롤러의 접근에 관여한다는 점에서 유사하다.차이점은 필터는 스프링과 무관하게 서블릿 자원이고, 인터셉터는 스프링의 빈으로 관리되면서 스프링의 컨텍스트 내에 속한다는 점
일반 필터의 경우, 현재 실행되는 서블릿 컨텍스트에서 속하기는 하지만 스프링과는 무관하다.
반면 인터셉터의 경우 스프링의 빈으로 관리되면서 스프링의 컨텍스트 내에 속하게 되어 스프링 내부에서 컨트롤러를 호출할 때 관여하기 때문에 스프링의 컨텍스트 내의 모든 자원을 활용 가능하다
사용자가 시스템의 리소스에 접근하기 위해서는 가장 먼저 인증 관리 필터의 검증을 통과해야 한다.
인증관리 필터가 사용자의 입력 정보를 기반으로 인증 처리를 하기 위해서는 사용자 정보가 저장된 객체가 필요한데, 이 때 사용되는 객체가 UserDetails 객체이다.
또한, UserDetails 객체에 실제 데이터베이스에서 검색한 사용자 정보를 저장하기 위해서는 UserDetailsService 객체가 필요하다
인증 관리자는 UserDetailsService 객체를 통해 UserDetails 객체를 획득하고, 이 UserDetails 객체에서 인증과 인가에 필요한 정보들을 추출하여 사용한다.
개발 환경
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.7.3</version>
</dependency>
버전은 스프링 프레임워크랑 호환되도록 맞춰주고, 시큐리티끼리는 동일 버전 사용하도록 설정해준다.
스프링 시큐리티는 단독 설정이 가능해서 설정 파일을 따로 빼두는 것이 관리하기 편하다.
root-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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
<security:http>
<security:form-login/>
</security:http>
<security:authentication-manager />
</beans>
스프링 시큐리티가 스프링 MVC에서 사용되기 위해서는 필터를 이용해서 스프링 동작에 관여하도록 해둔다.
<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>
<!-- ... 생략 ... -->
<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>
그리고 필터이름 꼭 저렇게 맞춰야한다.. 아니면 못 찾음... 샹 ㅋㅋ
URI | 설명 |
---|---|
/sample/all | 모두 접근 가능 |
/sample/member | 로그인한 사용자만 접근 가능 |
/sample/admin | 로그인 한 사용자 중 관리자 권한을 가진 사용자만 접근 가능 |
SampleController 작성
package com.board.controller;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Log4j2
@RequestMapping("/sample/*")
@Controller
public class SampleController {
@GetMapping("/all")
public void doAll(){
log.info("SampleController - doAll can access everyone");
}
@GetMapping("/member")
public void doMember(){
log.info("SampleController - doMember can access member");
}
@GetMapping("/admin")
public void doAdmin(){
log.info("SampleController - admin");
}
}
컨트롤러 작성 후 각 메서드와 일치하는 JSP 파일을 생성한다. (WEB-INF/views/sample/ ... )
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:intercept-url />
설정 후 /sample/member로 이동하면 로그인 페이지로 이동된다. (스프링 시큐리티의 기본 로그인 페이지)