jwt.....
후
jwt는 쉽지않습니다.
왜냐 jwt 버전이 10버전, 11버전 두개 있는데 두버전이 완전 다름
ㅋㅋㅋ
그래서 버전 잘 보고 해야함
package com.example.React_Native_back.springjwt.config;
import java.util.Collections;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import com.example.React_Native_back.springjwt.jwt.JWTFilter;
import com.example.React_Native_back.springjwt.jwt.JWTUtil;
import com.example.React_Native_back.springjwt.jwt.LoginFilter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSecurity
//@RequiredArgsConstructor
public class SecurityConfig {
//AuthenticationManager가 인자로 받을 AuthenticationConfiguraion 객체 생성자 주입
private final AuthenticationConfiguration authenticationConfiguration;
private final JWTUtil jwtUtil;
// cors 추가
// private final CorsFilter corsFilter;
public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil, CorsFilter corsFilter) {
this.authenticationConfiguration = authenticationConfiguration;
this.jwtUtil = jwtUtil;
// this.corsFilter = corsFilter;
}
// public class CustomDsl extends AbstractHttpConfigurer<CustomDsl, HttpSecurity> {
//
// @Override
// public void configure(HttpSecurity builder) {
// AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
// builder.addFilter(corsFilter); // 추가한 코드
//
// }
// }
//AuthenticationManager Bean 등록
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//csrf disable
http
.csrf((auth) -> auth.disable());
//From 로그인 방식 disable
http
.formLogin((auth) -> auth.disable());
//http basic 인증 방식 disable
http
.httpBasic((auth) -> auth.disable());
//경로별 인가 작업
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/api/login", "/").permitAll()
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated());
//JWTFilter 등록
http
.addFilterBefore(new JWTFilter(jwtUtil), LoginFilter.class);
//필터 추가 LoginFilter()는 인자를 받음 (AuthenticationManager() 메소드에 authenticationConfiguration 객체를 넣어야 함) 따라서 등록 필요
http
.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class);
//세션 설정
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http
.cors((corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:8080"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(Collections.singletonList("Authorization"));
return configuration;
}
})));
return http.build();
}
}
package com.example.React_Native_back.springjwt.jwt;
import java.io.IOException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import com.example.React_Native_back.springjwt.dto.CustomUserDetails;
import com.example.React_Native_back.springjwt.dto.UserDTO;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class JWTFilter extends OncePerRequestFilter {
private final JWTUtil jwtUtil;
public JWTFilter(JWTUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//request에서 Authorization 헤더를 찾음
String authorization= request.getHeader("Authorization");
//Authorization 헤더 검증
if (authorization == null || !authorization.startsWith("Bearer ")) {
System.out.println("JWTFilter Authorization token Null");
filterChain.doFilter(request, response);
//조건이 해당되면 메소드 종료 (필수)
return;
}
System.out.println("authorization now");
//Bearer 부분 제거 후 순수 토큰만 획득
String token = authorization.split(" ")[1];
//토큰 소멸 시간 검증
if (jwtUtil.isExpired(token)) {
System.out.println("token expired");
filterChain.doFilter(request, response);
//조건이 해당되면 메소드 종료 (필수)
return;
}
//토큰에서 username과 role 획득
String username = jwtUtil.getUsername(token);
// String password = jwtUtil.getPassword(token);
// System.out.println("password :: " + password);
String role = jwtUtil.getRole(token);
//userEntity를 생성하여 값 set
UserDTO userDTO = new UserDTO();
userDTO.setUsername(username);
userDTO.setPassword("1");
userDTO.setRole(role);
//UserDetails에 회원 정보 객체 담기
CustomUserDetails customUserDetails = new CustomUserDetails(userDTO);
//스프링 시큐리티 인증 토큰 생성
Authentication authToken = new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities());
//세션에 사용자 등록
SecurityContextHolder.getContext().setAuthentication(authToken);
filterChain.doFilter(request, response);
}
}
package com.example.React_Native_back.springjwt.jwt;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Jwts;
@Component
public class JWTUtil {
private SecretKey secretKey;
public JWTUtil(@Value("${spring.jwt.secret}")String secret) {
secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm());
}
public String getUsername(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class);
}
public String getRole(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class);
}
public Boolean isExpired(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date());
}
public String createJwt(String username, String role, Long expiredMs) {
return Jwts.builder()
.claim("username", username)
.claim("role", role)
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + expiredMs))
.signWith(secretKey)
.compact();
}
}
package com.example.React_Native_back.springjwt.jwt;
import java.util.Collection;
import java.util.Iterator;
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.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.example.React_Native_back.springjwt.config.SecurityConfig;
import com.example.React_Native_back.springjwt.dto.CustomUserDetails;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JWTUtil jwtUtil;
public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {
this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = obtainUsername(request);
String password = obtainPassword(request);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, null);
return authenticationManager.authenticate(authToken);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) {
CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();
String username = customUserDetails.getUsername();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority auth = iterator.next();
String role = auth.getAuthority();
String token = jwtUtil.createJwt(username, role, 60*60*10L);
response.addHeader("Authorization", "Bearer " + token);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
response.setStatus(401);
}
}
package com.example.React_Native_back.springjwt.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.example.React_Native_back.springjwt.dto.CustomUserDetails;
import com.example.React_Native_back.springjwt.dto.UserDTO;
import com.example.React_Native_back.springjwt.jwt.JWTUtil;
import com.example.React_Native_back.springjwt.repository.UserRepository;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService{
private final JWTUtil jwtUtil;
private final UserRepository userRepository;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
// public String authenticate(String username, String password) throws BadCredentialsException {
//
// // 받아온 유저네임과 패스워드를 이용해 UsernamePasswordAuthenticationToken 객체 생성
// UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
//
// // authenticationToken 객체를 통해 Authentication 객체 생성
// // 이 과정에서 재정의한 loadUserByUsername 메서드 호출
// Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
//
// // 인증 정보를 기준으로 jwt access 토큰 생성
// String accessToken = jwtUtil.createToken(authentication);
//
// return accessToken;
//
// }
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("loadUserByUsername : "+ username);
//DB에서 조회
UserDTO userData = userRepository.findByUsername(username);
if (userData != null) {
String password = passwordEncoder.encode(userData.getPassword());
userData.setPassword(password);
//UserDetails에 담아서 return하면 AutneticationManager가 검증 함
return new CustomUserDetails(userData);
}
return null;
}
}
package com.example.React_Native_back.springjwt.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.filter.CorsFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod(HttpMethod.GET);
config.addAllowedMethod(HttpMethod.POST);
config.addAllowedMethod(HttpMethod.PUT);
config.addAllowedMethod(HttpMethod.DELETE);
config.addAllowedMethod(HttpMethod.HEAD);
config.addExposedHeader("Authorization"); // 추가한 코드
source.registerCorsConfiguration("/**", config); // 추가한 코드
return new CorsFilter(source);
}
}
backend mvc