AuthenticationFilter 에 로그인 성공했을 때 로직에서 UserDto를 반환받기
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
log.debug(((User)authResult.getPrincipal()).getUsername());
String userName = ((User)authResult.getPrincipal()).getUsername();
UserDto userDetails = userService.getUserDetailsByEmail(userName);
}
기존의 log만 찍었던 로직을 다음과 같이 입력 받은 userName 값으로 userId값을 받아올 수 있도록 변경한다.
여기서 userService에서 getUserDetailsByEmail(userName)이 정의 되지 않았기 때문에 정의해줘야한다
생성자에 UserService와 Enviroment값을 매개변수로 설정했기 때문에
WebSecurity에서 생성자로 Filter를 생성하는 부분에도 값을 넣어줘야한다.
private AuthenticationFilter getAuthenticationFilter(AuthenticationManager authenticationManager) {
return new AuthenticationFilter(authenticationManager, userService, env);
}
UserServiceImpl에서
@Override
public UserDto getUserDetailsByEmail(String email) {
UserEntity userEntity = userRepository.findByEmail(email);
if(userEntity == null)
throw new UsernameNotFoundException(email);
return new ModelMapper().map(userEntity, UserDto.class);
}
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
application.yml
token:
expiration_time: 86400000 #ms단위 하루설정
secret: user_token
toeken을 이용해준다
//로그인 성공했을 때 로직
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
log.debug(((User)authResult.getPrincipal()).getUsername());
String userName = ((User)authResult.getPrincipal()).getUsername();
UserDto userDetails = userService.getUserDetailsByEmail(userName);
String token = Jwts.builder()
.setSubject(userDetails.getUserId()) //token 내용
.setExpiration(new Date(System.currentTimeMillis() +
Long.parseLong(env.getProperty("token.expiration_time")))) //파기 날짜
.signWith(SignatureAlgorithm.HS512, env.getProperty("token.secret")) //token 생성 알고리즘과 키 값
.compact();
response.addHeader("token", token);
response.addHeader("userId", userDetails.getUserId()); //원래는 반환하지 않는 데이터이지만 확인용
}
또한
userservice에서 jwt를 통해 토큰을 발급 받는 기능을 추가했으니 gateway에도
jwt dependency추가해준다.
@Component
@Slf4j
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {
private Environment env;
//생성시 Config class를 상속받은 Factory로 넘겨줘야해서 lombok을 사용하지 않고 다음과 같이 처리
public AuthorizationHeaderFilter(Environment env) {
super(Config.class);
this.env = env;
}
public static class Config{}
//login -> token -> users (with token) -> header(include token)
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
//header에 HttpHeaders.AUTHORIZATION 값이 존재하는지 확인
if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
return onError(exchange, "no authorization header", HttpStatus.UNAUTHORIZED);
}
String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);//배열이라서
String jwt = authorizationHeader.replace("Bearer", "");
if(!isJwtValid(jwt)){
return onError(exchange, "JWT Token is not valid", HttpStatus.UNAUTHORIZED);
}
return chain.filter(exchange);
});
}
//token이 유효한지 확인
private boolean isJwtValid(String jwt) {
boolean returnValue = true;
String subject = null;
try {
subject = Jwts.parser().setSigningKey(env.getProperty("token.secret"))
.parseClaimsJws(jwt).getBody()
.getSubject();
//token의 내용을 가져옴
//jws로 파싱하고 그 앞에서 subject값
} catch (Exception ex){
returnValue = false;
}
if(subject == null || subject.equals("")){
returnValue = false;
}
return returnValue;
}
//에러 발생시 에러 값을 response
//Mono, Flux -> Spring WebFlux / 데이터 단위 단일=Mono, 복수=Flux
private Mono<Void> onError(ServerWebExchange exchange,
String err,
HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
log.error(err);
return response.setComplete();
}
}
그리고 yml에 AuthorizationHeaderFilter 설정해준다
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user-service/login
- Method=POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user-service/(?<segment>.*), /$\{segment}
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user-service/users
- Method=POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user-service/(?<segment>.*), /$\{segment}
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user-service/**
- Method=GET
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user-service/(?<segment>.*), /$\{segment}
- AuthorizationHeaderFilter
user-service중 GET방식에 AuthorizationHeaderFilter filter를 추가
Authenticate 필요X post-> /user-service/users (회원가입)
post-> /user-service/login (로그인)
Authenticate 필요O GET-> /user-service/users (회원정보)
Bearer Token에 발급 받은 토큰을 입력 후 진행 과정을 디버그로 확인해보자
AuthorizationHeaderFilter의 apply()로 요청이 들어가고
//header에 HttpHeaders.AUTHORIZATION 값이 존재하는지 확인
if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
return onError(exchange, "no authorization header", HttpStatus.UNAUTHORIZED);
}
이걸로 인해 Header에 Authorization : value 가 존재하는지 확인
value에 Bearer + token으로 값이 넘어오는데 해당 값을 replace()로 처리해주고 isJwtValid()를 통해 유효성을 체크