반응형
이 포스팅은 아래의 강의를 참고하였으니 여기에서 공부하시는 것을 추천드립니다.
[무료] 스프링부트 시큐리티 & JWT 강의 - 인프런 | 강의
스프링부트 시큐리티에 대한 개념이 잡힙니다., - 강의 소개 | 인프런...
www.inflearn.com
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)
개발한 API 테스트 도구 - postman
안녕하세요. 이번 포스팅에서는 개발한 API를 테스트하고 결과를 확인하는 도구인 postman에 대해서 포스팅 하겠습니다. 1. postman 가입 아래의 링크로 이동해서 편한 방법으로 가입 https://www.postman.c
ajdahrdl.tistory.com
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 |