반응형
이 포스팅은 아래의 강의를 참고하였으니 여기에서 공부하시는 것을 추천드립니다.
1. JWT 임시 토큰을 만들어서 기본 흐름 확인하기
1) MyFilter3 자바 파일 수정하기
이전 포스팅에서 생성한 MyFilter3 자바 파일에 토큰을 확인하는 로직을 추가
package com.cos.security1.config.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class MyFilter3 implements Filter {
// 토큰 값을 확인하는 필터
// 토큰 생성 시점: 정상적으로 로그인 시, 토큰을 만들어주고 토큰을 알려줌 => 사용자가 요청할 때마다 header에 Authorization에 value값으로 토큰을 가지고 요청 => 그 때 토큰이 유효한 지 검증하는 로직
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println("filter3");
// POST 요청일 때만 확인
if(req.getMethod().equals("POST")){
String headerAuth = req.getHeader("Authorization"); // Header의 name이 'Authorization' 인 value
System.out.println(headerAuth);
if(headerAuth != null && headerAuth.equals("cos")){ // Authorization이 'cos' 면 토큰을 가지고 있는 것으로 간주
chain.doFilter(req,res);
}
else{ // 아니라면 인증이 안 된것
PrintWriter outPrintWriter = res.getWriter();
outPrintWriter.println("인증X");
}
}
}
}
2) RestControllerAPI에 아래의 함수 추가 (아무 Controller에 추가해도 상관 X)
@PostMapping("token")
public String Token(){
return "<h1>token</h1>";
}
3) 테스트
a) header의 Authorization의 value에 cos가 아닌 경우
b) header의 Authorization의 value에 cos인 경우
참고) POST 요청 테스트 방법 : postman 이용(https://ajdahrdl.tistory.com/255)
2. 로그인 시, JWT 토큰을 생성해서 반환해주기
1) PrincipalDetails 생성
package com.cos.jwt.security1.config.jwtAuth;
import com.cos.jwt.security1.model.User;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
@Data
public class PrincipalDetails implements UserDetails {
private User user;
public PrincipalDetails(User user){
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
user.getRoleList().forEach(r->{
authorities.add(()->r);
});
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
2) PrincipalDetailsJwtService 생성
package com.cos.jwt.security1.config.jwtAuth;
import com.cos.jwt.security1.Repository.UserRepository;
import com.cos.jwt.security1.model.User;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class PrincipalDetailsJwtService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("jwtAuth.PrincipalDetailsService.loadUserByUsername");
// **** 로그인 과정 **** //
// 1. username과 password를 받고
// 2. 정상인지 확인. authentcationManager로 로그인 시도를 하면 PrincipalDetailsJwtService의 loadUserByUsername이 실행됨
// 3. PrincipalDetails를 세션에 담고(권한 관리를 위해서. 권한같은 정보가 없다면 세션이 필요없음)
// 4. JWT 토큰을 만들고 응답
User userEntity = userRepository.findByUsername(username);
System.out.println("찾은 UserEntity : " + userEntity.toString());
PrincipalDetails p = new PrincipalDetails(userEntity);
return p;
}
}
3) JwtAuthenticationFilter 생성
package com.cos.jwt.security1.config.jwtConfig;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.cos.jwt.security1.config.jwtAuth.PrincipalDetails;
import com.cos.jwt.security1.model.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
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.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
// spring security에서 UsernamePasswordAuthenticationFilter 가 있는데
// login 요청 시, username, pw 전송 시, UsernamePasswordAuthenticationFilter가 동작함
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
// /login 요청을 하면 로그인 시도를 위해서 실행되는 함수
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("JwtAuthenticationFilter.attemptAuthentication : 로그인 시도중입니다");
try{
// 1. usernamee, pw 받기
// request로 부터 온 데이터들 확인 방법
/*
BufferedReader br = request.getReader();
String input = null;
while(true){
input = br.readLine();
if(input == null){
break;
}
System.out.println(input);
}
*/
// request로 부터 온 데이터(JSON으로 보냈다는 가정 하에) 를 JSON 형태로 변환하기
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(request.getInputStream(), User.class);
System.out.println(user.toString());
System.out.println("========================================1");
// 토큰 생성 (폼 로그인 시에는 자동으로 해줌)
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
System.out.println("========================================2");
// PrincipalDetailsJwtService의 loadUserByUsername() 함수가 실행됨
Authentication authentication = authenticationManager.authenticate(authenticationToken);
System.out.println("========================================3");
// 로그인을 성공했다는 뜻
PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal();
System.out.println("로그인 한 사용자 : " + principal.getUser().getUsername());
System.out.println("========================================4");
// authentication 객체가 session 영역에 저장
// 리턴하는 이유는 권한 관리를 security가 대신 해주기 때문에 편하기 위해서.
// JWT 토큰을 사용한다면 세션을 사용할 필요가 없지만 권한 관리 때문에 세션을 사용하는 것
return authentication;
} catch(IOException e){
e.printStackTrace();
}
return null;
}
// attemptAuthentication 실행 후 인증이 정상적으로 완료되면 successfulAuthentication 함수가 실행됨
// JWT 토큰을 만들어서 request 요청한 사용자에게 JWT 토큰을 주면 된다.
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
System.out.println("successfulAuthetication 실행 : 인증 완료란 뜻");
PrincipalDetails principalDetails = (PrincipalDetails) authResult.getPrincipal();
// JWT 토큰 만들기
// RSA 방식이 아닌, Hash암호방식 (최근 이 방식을 더 많이 사용한다고 함)
String jwtToken = JWT.create()
.withSubject("토큰 제목") // 토큰 제목
.withExpiresAt(new Date(System.currentTimeMillis()+(60000*10))) // 토큰 만료시간 ms 기준 60초 * 10 (10분)
.withClaim("id", principalDetails.getUser().getId())
.withClaim("username", principalDetails.getUser().getUsername())
.sign(Algorithm.HMAC512("cos"));
response.addHeader("Authentication", "Bearer " + jwtToken);
}
}
4) 결과 확인
반응형
'BE > Spring' 카테고리의 다른 글
[Spring Security] React(Front)와 Spring Security 설정 방법 (2) | 2022.07.19 |
---|---|
[Spring Security] JWT - 4. JWT 로그인 및 권한 처리 (0) | 2022.06.19 |
[Spring Security] JWT - 2. JWT 필터 설정 및 필터 우선순위 적용 방법 (0) | 2022.06.11 |
[Spring Security] JWT - 1. JWT를 공부하기 전에 (0) | 2022.06.09 |
[Spring Security] 네이버 로그인하기 (0) | 2022.06.06 |