티스토리 뷰

1. unauthorizedHandler 비인증 처리

- AuthEntryPointJwt

@Component  // 인증,인가 예외발생시 처리
public class AuthEntryPointJwt implements AuthenticationEntryPoint {

	private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
	
	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException {
		
		logger.error("Unauthorized error: {}", authException.getMessage());
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "가입하지 않은 아이디이거나, 잘못된 비밀번호입니다.");  
	}
}

- 인증과정에서 실패하거나 인증헤더(Authorization)를 보내지 않게되는 경우 401 라는 응답값을 받게된다. 
이를 처리해주는 로직이 AuthenticationEntryPoint이다.
React의 로그인 페이지에서 회원 정보가 없을 경우 에러 메세지를 주기 위해서 response.sendError( ) 쓴다.

 

2. JwtToken 생성

- JwtToken

@Component
public class JwtUtils {
	private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);

	@Value("${jwt.app.jwtSecret}")
	private String jwtSecret;

	@Value("${jwt.app.jwtExpirationMs}")
	private int jwtExpirationMs;

	public String generateJwtToken(Authentication authentication) {

		UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();

		return Jwts.builder()
				.setSubject((userPrincipal.getUsername()))   // payload
				.setIssuedAt(new Date())    
				.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
				.signWith(SignatureAlgorithm.HS512, jwtSecret)
				.compact();
		
		
	}

	public String getUserNameFromJwtToken(String token) {
		return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
	}

- Authentication 객체의 getPrincipal() 메서드를 실행하게 되면, UserDetails를 구현한 사용자 객체를 Return 한다.
UserDetails를 구현한 객체가 가지고 있는 정보들을 가져올 수 있다. 가져온 정보들을 Jwt 토큰에 builder( ) 하여 이름, 현재 시간, 토큰 시간, 개인 비밀키를 넣어 토큰을 생성한다.
- getUserNameFromJwtToken(Strign token)는 인증된 회원들을 loadUserByUsername(username)에 담기 위함이다.

 

3. AuthTokenFilter 생성

- AuthTokenFilter 

public class AuthTokenFilter extends OncePerRequestFilter {
.....
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
	throws ServletException, IOException {
	try {
		String jwt = parseJwt(request);
		if (jwt != null) {
				
		String username = jwtUtils.getUserNameFromJwtToken(jwt);
				
		UserDetails userDetails = userDetailsService.loadUserByUsername(username);
		UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
		authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

		SecurityContextHolder.getContext().setAuthentication(authentication);  // 현재 세션의 사용자 정보를 가져옴
		}
	} catch (Exception e) {
		logger.error("Cannot set user authentication: {}", e);
	}
	filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
		String headerAuth = request.getHeader("Authorization");

		if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
			return headerAuth.substring(7, headerAuth.length());
		}
		return null;
	}
}

- header에서 Authorization 값을 가져오고 그 값이 있거나 startWith 시작하는 부분이 Bearer로 시작한다면 headerAuth.substring 리턴한다. 

- doFilterInternal에서 Jwt 토큰이 있다면 authentication에 username, role를 넣고 수동으로 인증을 설정하도록 스프링 시큐리티를 구성한다. 

- SecurityContextHolder( ) 통해 현재 사용자 정보를 authentication 통해 담아준다.