2024. 12. 6. 10:55ㆍ카테고리 없음
이제 JWT TOKEN을 사용하기 위한 설정에 들어가도록 한다.
우선, 이전에 했던 내용들에 하나씩 추가해 나가도록 한다.
참고로 해당 파일을 포함하여 구현하는 방식은 다 각각 다르기 때문에 참고만 하길 바란다.
내가 작업한 Token 관리는 DB를 통한 관리가 아닌 Redis를 통해서 관리 한다.
그래서 나는 Docker형식이 아닌 WSL을 이용하여 Local에서 작업하였다.
아래 내용을 참조하면 조금 도움이 되지 않을까 한다.
https://kamsi76.tistory.com/entry/Windows-11-%EC%97%90-Redis-%EC%84%A4%EC%A0%95
Windows 11 에 Local Redis 설정(Docker 아님)
Spring Boot3 + Security + JWT 를 구성하려다 보니 JWT에서 refresh Token 의 저장소를 구성하기 위해 Redis를 가 필요 했다.그래서 구글링 + ChatGTP를 이용하여 구성했다. 구성 방식은 아래와 같다.1. Linux용 Windo
kamsi76.tistory.com
주요 추가 파일
1. JwtTokenProvider.java
JWT Token을 관리해 주는 Provider
package kr.co.infob.config.security.provider;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.crypto.SecretKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration.access}")
private long accessTokenExpiration;
@Value("${jwt.expiration.refresh}")
private long refreshTokenExpiration;
private SecretKey key;
@PostConstruct
public void init() {
key = Keys.hmacShaKeyFor(secret.getBytes());
}
/**
* token 사용자 모든 속성 정보 조회
*
* @param token JWT
* @return All Claims
*/
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.verifyWith(key)
.build()
.parseSignedClaims(token)
.getPayload();
}
/**
* 토큰 유효성 검토
*
* @param token
* @return
*/
public Boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
return true;
} catch (SecurityException | MalformedJwtException | ExpiredJwtException | UnsupportedJwtException
| IllegalArgumentException e) {
log.warn("JWT Exception: {}", e.getMessage());
return false;
}
}
/**
* token 사용자 속성 정보 조회
*
* @param token JWT
* @param claimsResolver Get Function With Target Claim
* @param <T> Target Claim
* @return 사용자 속성 정보
*/
private <T> T getClaimFromToken(final String token, final Function<Claims, T> claimsResolver) {
// token 유효성 검증
if (Boolean.FALSE.equals(validateToken(token)))
return null;
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
/**
* token Username 조회
*
* @param token JWT
* @return token Username
*/
public String getUsernameFromToken(final String token) {
return getClaimFromToken(token, Claims::getSubject);
}
/**
* 토큰 만료 일자 조회
*
* @param token JWT
* @return 만료 일자
*/
public Date getExpirationDateFromToken(final String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
/**
* 토큰 만료 일자 조회
*
* @param token JWT
* @return 만료 일자
*/
public long getExpirationTimeFromToken(final String token) {
Date expiration = getExpirationDateFromToken(token);
return expiration.getTime();
}
/**
* 토큰 만료여부 조회
*
* @param token
* @return
*/
public Boolean isTokenExpired(String token) {
Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
/**
* 토큰 만료 잔여 시간
*
* @param token
* @return
*/
public long getRemainMilliSeconds(String token) {
Date expiration = getExpirationDateFromToken(token);
Date now = new Date();
return expiration.getTime() - now.getTime();
}
/**
* JWT token 생성
*
* @param id token 생성 id
* @param claims token 생성 claims
* @return token
*/
private String doGenerateToken(final String id, final Map<String, Object> claims, final long expireTime) {
Date currentDate = new Date();
Date accessTokenExpiresIn = new Date(currentDate.getTime() + expireTime);
return Jwts.builder()
.subject(id)
.claims(claims)
.issuedAt(new Date())
.expiration(accessTokenExpiresIn)
.signWith(key).compact();
}
/**
* Spring Security 인증정보를 통한 생성
*
* @param authentication
* @return
*/
public String generateAccessToken(Authentication authentication) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String id = token.getName();
return generateAccessToken(id);
}
/**
* access token 생성
*
* @param id token 생성 id
* @return access token
*/
public String generateAccessToken(final String id) {
return generateAccessToken(id, new HashMap<>());
}
/**
* access token 생성
*
* @param id token 생성 id
* @param claims token 생성 claims
* @return access token
*/
public String generateAccessToken(final String id, final Map<String, Object> claims) {
return doGenerateToken(id, claims, accessTokenExpiration);
}
/**
* Spring Security 인증정보를 통한 Refresh Token 생성
*
* @param authentication
* @return
*/
public String generateRefreshToken(Authentication authentication) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String id = token.getName();
return generateRefreshToken(id);
}
/**
* refresh token 생성
*
* @param id token 생성 id
* @return refresh token
*/
public String generateRefreshToken(final String id) {
return generateRefreshToken(id, new HashMap<>());
}
/**
* refresh token 생성
*
* @param id token 생성 id
* @param claims token 생성 claims
* @return refresh token
*/
public String generateRefreshToken(final String id, final Map<String, Object> claims) {
return doGenerateToken(id, new HashMap<>(), refreshTokenExpiration);
}
/**
* Header에 저장된 Token 정보 조회
*
* @param header
* @return
*/
public String getHeaderToken(String header) {
String token = null;
if (header != null && header.startsWith("Bearer ")) {
token = header.substring(7);
}
return token;
}
}
해당 파일에 대해서는 별도의 설명은 하지 않는다.
2. JwtAuthorizationFilter.java
package kr.co.infob.config.security.filter;
import java.io.IOException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import kr.co.infob.common.exception.RefreshTokenException;
import kr.co.infob.common.exception.RefreshTokenException.ErrorCase;
import kr.co.infob.config.security.provider.JwtTokenProvider;
import kr.co.infob.config.security.service.RedisService;
import kr.co.infob.config.security.service.SecurityService;
import kr.co.infob.config.security.vo.TokenVo;
import kr.co.infob.config.security.vo.UserDetailsVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* JwtAuthenticationFilter 역할
* JWT Token을 이용하여 인증 및 권한 부여를 처리하는 필터
*
* 1. 요청 해더에서 JWT 토튼 추출
* 2. JWT 검증처리(Token 유효성, 사용자 정보, 만료여부)
* 3. 토큰은 있는데 SecurityContext에 없는 경우 사용자 정보를 조회해서 SecurityContext에 저장
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
private final SecurityService securityService;
private final RedisService redisService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String headerAccessToken = jwtTokenProvider.getHeaderToken(httpServletRequest.getHeader("Authorization"));
String headerRefreshToken = jwtTokenProvider
.getHeaderToken(httpServletRequest.getHeader("x-refresh-token"));
/*
* AccessToken 또는 refreshToken이 살아 있는 경우
*
* 1. refreshToken이 Blacklist에 포함되어 있는지 여부 확인 포함되어 있으면 다시 로그인 처리 필요(403)
*
* 2. AccessToken 존재 및 적합성, 만료여부 확인
* 정상 :
* 1-1. refreshToken과 accessToken 아이디가 일치여부 확인 일치하지 않은 경우 다시 로그인 필요(403)
* 1-2. AccessToken이 있고 AccessToken이 유효한 Token이면 다음 필터로 이동
* 비정상 : 1. accessToken 재 생성
*/
if (StringUtils.hasText(headerAccessToken) || StringUtils.hasText(headerRefreshToken)) {
if (redisService.isBlacklisted(headerRefreshToken)) {
throw new RefreshTokenException(ErrorCase.BLACKLIST);
}
String username = null;
boolean validate = false;
username = jwtTokenProvider.getUsernameFromToken(headerAccessToken);
validate = jwtTokenProvider.validateToken(headerAccessToken);
// 정상인 경우
if (StringUtils.hasText(username) && validate) {
// accessToken과 headerRefreshToken을 사용하여 비교 처리해서 문제가 있는 경우 다시 로그인 처리
if (!validateRefresh(username, headerRefreshToken)) {
throw new RefreshTokenException(ErrorCase.BAD_REFRESH);
}
String refreshUsername = jwtTokenProvider.getUsernameFromToken(headerRefreshToken);
UserDetailsVo userDetails = securityService.loadUserByUsername(refreshUsername);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
} else {
/*
* refreshToken 정보가 정상이면 accessToken을 재발급한다.
*/
String refreshUsername = jwtTokenProvider.getUsernameFromToken(headerRefreshToken);
UserDetailsVo userDetails = securityService.loadUserByUsername(refreshUsername);
if (!ObjectUtils.isEmpty(userDetails)) {
String accessToken = jwtTokenProvider.generateAccessToken(userDetails.getUsername());
// 사용자에게 전달
TokenVo token = TokenVo.builder().accessToken(accessToken).build();
throw new RefreshTokenException(ErrorCase.BAD_ACCESS, token);
}
}
}
} catch (RefreshTokenException e) {
log.error(e.getMessage(), e);
e.sendResponseError(response);
}
}
/**
* RefreshToken 유효성 체크 refreshToken의 유효성을 체크한다.
* accessToken이 있는 경우 accessToken과 비교를 하고 없는 경우 refreshToken 자체로 비교한다.
*
* @param accessUsername AccessToken username
* @param headerRefreshToken
* @return
*/
private boolean validateRefresh(String accessUsername, String headerRefreshToken) {
if (!StringUtils.hasText(headerRefreshToken))
return false;
/*
* 1. Header RefreshToken Blacklist 등록 여부와 유효성 체크 및 만료 여부 체크
* 1-1 정상이면
* 1-1-2. Redis의 refreshToken과 header refreshToken의 username이 동일한지 확인
* 2. AccessToken의 username 있는 경우 AccessToken username과 RefreshToken의 username이 같은지 확인
*/
String refreshUsername = jwtTokenProvider.getUsernameFromToken(headerRefreshToken);
boolean validate = jwtTokenProvider.validateToken(headerRefreshToken);
if (StringUtils.hasText(refreshUsername) && validate) {
String refreshToken = redisService.getRefreshToken(accessUsername);
if (!headerRefreshToken.equals(refreshToken)) {
return false;
}
}
if (StringUtils.hasText(accessUsername)) {
if (!accessUsername.equals(refreshUsername)) {
return false;
}
}
return true;
}
}
3. CustomAuthenticationFilter.java
UsernamePasswordAuthenticationFilter를 확장한 Class로 사용자 정보를 JSON으로 받아서 인증처리 하도록 수정하였다.
package kr.co.infob.config.security.filter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import kr.co.infob.common.database.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public CustomAuthenticationFilter(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
/**
* 지정된 URL로 form 전송을 하였을 경우 파라미터 정보를 가져온다.
*
* @param request from which to extract parameters and perform the authentication
* @param response the response, which may be needed if the implementation has to do a redirect as part of a multi-stage authentication process (such as OpenID).
* @return Authentication {}
* @throws AuthenticationException {}
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
UsernamePasswordAuthenticationToken authRequest;
try {
authRequest = getAuthRequest(request);
setDetails(request, authRequest);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* Request로 받은 ID와 패스워드 기반으로 토큰을 발급한다.
*
* @param request HttpServletRequest
* @return UsernamePasswordAuthenticationToken
* @throws Exception e
*/
private UsernamePasswordAuthenticationToken getAuthRequest(HttpServletRequest request) throws Exception {
try {
JsonFactory factory = new JsonFactory();
factory.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
ObjectMapper mapper = new ObjectMapper(factory);
UserInfo user = mapper.readValue(request.getInputStream(), UserInfo.class);
log.debug("CustomAuthenticationFilter :: userId:" + user.getUserId() + " userPw:" + user.getPasswd());
// ID와 암호화된 패스워드를 기반으로 토큰 발급
return new UsernamePasswordAuthenticationToken(user.getUserId(), user.getPasswd());
} catch (UsernameNotFoundException ae) {
throw new UsernameNotFoundException(ae.getMessage());
}
}
}
4. CustomAuthSuccessHandler.java
package kr.co.infob.config.security.handler;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import kr.co.infob.common.utils.RequestUtil;
import kr.co.infob.common.vo.Response;
import kr.co.infob.config.security.provider.JwtTokenProvider;
import kr.co.infob.config.security.service.RedisService;
import kr.co.infob.config.security.vo.TokenVo;
import lombok.extern.slf4j.Slf4j;
/**
* 로그인이 성공한 경우 처리하는 Handler
* 사용자 정보 token에서 인증정보를 통하여
* AccessToken과 RefreshToken 을 생성하고
* RefreshToken을 Redis에 저장하고 사용자에게 Token 정보를 전달한다.
*/
@Component
@Slf4j
public class CustomAuthSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private RedisService redisService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
UsernamePasswordAuthenticationToken user = (UsernamePasswordAuthenticationToken)authentication;
String accessToken = jwtTokenProvider.generateAccessToken(authentication);
String refreshToken = jwtTokenProvider.generateRefreshToken(authentication);
redisService.saveRefreshToken(user.getName(), refreshToken, jwtTokenProvider.getExpirationTimeFromToken(refreshToken));
Response<?> res = Response.success(TokenVo.of(accessToken, refreshToken));
RequestUtil.printJsonResponse(response, res);
}
}
SecurityConfig 내용 변경
CustomAuthenticationFilter 추가
/**
* 로그인한 사용자 정보를 확인을 위한 Provider 설정
* @param http
* @return
* @throws Exception
*/
@Bean
AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(authenticationProvider);
return authenticationManagerBuilder.build();
}
/**
* UsernamePasswordAuthenticationFilter를 확장한 CustomeAuthenticationFilter
* Form 기반의 UsernamePasswordAuthenticationFilter를 JSON 형태로 변경하여 사용자 정보를 처리함.
* @param http
* @return
* @throws Exception
*/
@Bean
CustomAuthenticationFilter customAuthenticationFilter(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManagerBuilder.class).build();
CustomAuthenticationFilter filter = new CustomAuthenticationFilter(authenticationManager);
filter.setFilterProcessesUrl("/api/v1/auth/signin");
filter.setAuthenticationSuccessHandler(customAUthSuccessHandler);
filter.setAuthenticationFailureHandler(customAuthFailureHandler);
return filter;
}
CusomAuthenticationFilter에 AuthenticationManager를 전달하기 위해 AuthenticationManager도 생성하여 CusomAuthenticationFilter 생성 시 전달한다.
@Bean
SecurityFilterChain filterChain(HttpSecurity http, CustomAuthenticationFilter customAuthenticationFilter) throws Exception {
http
...
.addFilterBefore(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
// JWT 인증 (커스텀 필터)
.addFilterBefore(jwtAuthorizationFilter, BasicAuthenticationFilter.class)
...
return http.build();
}
FilterChain에 추가 처리
스프링 셋팅은 여기까지 끝.
SecurityConfig.java(전체 소스)
package kr.co.infob.config.security;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import kr.co.infob.config.security.authorization.CustomAuthorizationManager;
import kr.co.infob.config.security.filter.CustomAuthenticationFilter;
import kr.co.infob.config.security.filter.JwtAuthorizationFilter;
import kr.co.infob.config.security.handler.CustomAuthSuccessHandler;
import kr.co.infob.config.security.handler.CustomAccessDeniedHandler;
import kr.co.infob.config.security.handler.CustomAuthFailureHandler;
import kr.co.infob.config.security.handler.CustomAuthenticationEntryPoint;
import kr.co.infob.config.security.intercept.CustomSecurityMetadataSource;
import kr.co.infob.config.security.provider.CustomAuthenticationProvider;
import kr.co.infob.config.security.service.SecurityService;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final SecurityService securityService;
private final JwtAuthorizationFilter jwtAuthorizationFilter;
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
private final CustomAccessDeniedHandler customAccessDeniedHandler;
private final CustomAuthSuccessHandler customAUthSuccessHandler;
private final CustomAuthFailureHandler customAuthFailureHandler;
private final CustomAuthenticationProvider authenticationProvider;
@Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http, CustomAuthenticationFilter customAuthenticationFilter) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
//Login, Logout 등 기본 방식 미사용 처리
.formLogin(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
//JWT를 사용할 예정으로 Session 사용하지 않음
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
//요청 URL 검증 추리
.authorizeHttpRequests(requests ->
requests
.anyRequest()
.access(customAuthorizationManager())
)
.addFilterBefore(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
// JWT 인증 (커스텀 필터)
.addFilterBefore(jwtAuthorizationFilter, BasicAuthenticationFilter.class)
// Exception 처리
.exceptionHandling(exception -> exception
.authenticationEntryPoint(customAuthenticationEntryPoint)
.accessDeniedHandler(customAccessDeniedHandler)
);
return http.build();
}
/**
* 권한정보에 자동으로 붙는 기본 접두사(ROLE_) 제거
* @return
*/
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("");
}
/**
* 권한 및 엑세스 제어 시 기본 접두사(ROLE_) 제거
* @return
*/
@Bean
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultWebSecurityExpressionHandler;
}
/**
* 로그인한 사용자 정보를 확인을 위한 Provider 설정
* @param http
* @return
* @throws Exception
*/
@Bean
AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(authenticationProvider);
return authenticationManagerBuilder.build();
}
/**
* UsernamePasswordAuthenticationFilter를 확장한 CustomeAuthenticationFilter
* Form 기반의 UsernamePasswordAuthenticationFilter를 JSON 형태로 변경하여 사용자 정보를 처리함.
* @param http
* @return
* @throws Exception
*/
@Bean
CustomAuthenticationFilter customAuthenticationFilter(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManagerBuilder.class).build();
CustomAuthenticationFilter filter = new CustomAuthenticationFilter(authenticationManager);
filter.setFilterProcessesUrl("/api/v1/auth/signin");
filter.setAuthenticationSuccessHandler(customAUthSuccessHandler);
filter.setAuthenticationFailureHandler(customAuthFailureHandler);
return filter;
}
/**
* 인증정보를 통한 승인 여부를 관리하는 Manager
* Database에 저장된 정보와 사용자 로그인정보를 비교하여 접근 가능여부를 확인한다.
* @return
*/
@Bean
AuthorizationManager<RequestAuthorizationContext> customAuthorizationManager() {
return new CustomAuthorizationManager(customSecurityMetadataSource());
}
/**
* Database에서 관리하는 URL 정보를 조회하여 현재 접속한 URL의 권한 정보를 반환한다.
* FilterInvocationSecurityMetadataSource 구현한 class
* @return
*/
CustomSecurityMetadataSource customSecurityMetadataSource() {
LinkedHashMap<RequestMatcher, List<ConfigAttribute>> destMap = securityService.selectUrlRoleMapping();
return new CustomSecurityMetadataSource(destMap);
}
}