Search

JWT x SpringSecurity

JWT x SpringSecurity

β€’
build.gradle
β€’
νšŒμ›κ°€μž… 및 둜그인
β—¦
Users.java
β—¦
UserAuth.java
β—¦
UserMapper.xml
β—¦
UserMapper.java
β—¦
UserServicejava
β—¦
UserServiceImpl.java
β€’
인증
β—¦
JWT 토큰 인증
β–ͺ
SecurityConstants.java
β–ͺ
AuthenticationRequest.java
β–ͺ
JwtProps.java
β–ͺ
SecurityConfig.java
β–ͺ
CustomUser.java
β–ͺ
CustomUserDetailService.java
β—¦
토큰 Provider
β–ͺ
JwtTokenProvider.java
β€’
토큰 생성
β€’
토큰 해석
β€’
토큰 μœ νš¨μ„± 검사
β—¦
ν•„ν„° μ„€μ •
β–ͺ
JWT 토큰 μš”μ²­ ν•„ν„°
β€’
JwtRequestFilter.java
β–ͺ
JWT 토큰 인증 ν•„ν„°
β€’
JwtAuthenticationFilter.java

build.gradle

plugins { id 'java' id 'war' id 'org.springframework.boot' version '2.7.18' id 'io.spring.dependency-management' version '1.1.4' } group = 'com.aloha' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' // spring-boot-configuration-processor implementation 'org.springframework.boot:spring-boot-configuration-processor' // jwt implementation 'io.jsonwebtoken:jjwt-api:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3' } tasks.named('test') { useJUnitPlatform() }
Markdown
볡사

νšŒμ›κ°€μž… 및 둜그인

β€’
Users.java
β€’
UserAuth.java
β€’
UserMapper.xml
β€’
UserMapper.java
β€’
UserServicejava
β€’
UserServiceImpl.java

Users.java

@Data public class Users { private int no; private String userId; private String userPw; private String userPwCheck; // λΉ„λ°€λ²ˆν˜Έ 확인 private String name; private String email; private Date regDate; private Date updDate; private int enabled; // νœ΄λ©΄μ—¬λΆ€ // κΆŒν•œ λͺ©λ‘ List<UserAuth> authList; public Users() { } public Users(Users user) { this.no = user.getNo(); this.userId = user.getUserId(); this.userPw = user.getUserPw(); this.name = user.getName(); this.email = user.getEmail(); this.authList = user.getAuthList(); } }
Java
볡사

UserAuth.java

// νšŒμ› κΆŒν•œ @Data public class UserAuth { private int authNo; private String userId; private String auth; public UserAuth() { } public UserAuth(String userId, String auth) { this.userId = userId; this.auth = auth; } }
Java
볡사

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.aloha.jwt_security.mapper.UserMapper"> <!-- Users 맀핑--> <resultMap type="Users" id="userMap"> <id property="no" column="no" /> <result property="no" column="no" /> <result property="userId" column="user_id" /> <result property="userPw" column="user_pw" /> <result property="name" column="name" /> <result property="email" column="email" /> <result property="enabled" column="enabled" /> <result property="regDate" column="reg_date" /> <result property="updDate" column="upd_date" /> <collection property="authList" resultMap="authMap"></collection> </resultMap> <!-- UserAuth 맀핑 --> <resultMap type="UserAuth" id="authMap"> <result property="userId" column="user_id" /> <result property="auth" column="auth" /> </resultMap> <!-- νšŒμ› 등둝 --> <insert id="insert"> INSERT INTO user( user_id, user_pw, name, email ) VALUES ( #{userId}, #{userPw}, #{name} ,#{email} ) </insert> <!-- νšŒμ› 쑰회 --> <select id="select" resultType="Users"> SELECT * FROM user WHERE no = #{no} </select> <!-- νšŒμ› 쑰회 - id --> <select id="login" resultMap="userMap"> SELECT u.no ,u.user_id ,user_pw ,name ,email ,enabled ,reg_date ,upd_date ,auth FROM user u LEFT OUTER JOIN user_auth auth ON u.user_id = auth.user_id WHERE u.user_id = #{userId} </select> <!-- νšŒμ› κΆŒν•œ 등둝 --> <insert id="insertAuth"> INSERT INTO user_auth( user_id, auth ) VALUES ( #{userId}, #{auth} ) </insert> <!-- νšŒμ› μˆ˜μ • --> <update id="update"> UPDATE user SET user_id = #{userId} ,user_pw = #{userPw} ,name = #{name} ,email = #{email} WHERE user_id = #{userId} </update> <!-- νšŒμ› μ‚­μ œ --> <delete id="delete"> DELETE FROM user WHERE user_id = #{userId} </delete> </mapper>
XML
볡사

UserMapper.java

@Mapper public interface UserMapper { // νšŒμ› 등둝 public int insert(Users user) throws Exception; // νšŒμ› 쑰회 public Users select(int userNo) throws Exception; // μ‚¬μš©μž 인증(둜그인) - id public Users login(String username); // νšŒμ› κΆŒν•œ 등둝 public int insertAuth(UserAuth userAuth) throws Exception; // νšŒμ› μˆ˜μ • public int update(Users user) throws Exception; // νšŒμ› μ‚­μ œ public int delete(String userId) throws Exception; }
Java
볡사

UserServicejava

public interface UserService { // νšŒμ› 등둝 public int insert(Users user) throws Exception; // νšŒμ› 쑰회 public Users select(int userNo) throws Exception; // 둜그인 public void login(Users user, HttpServletRequest requset) throws Exception; // νšŒμ› μˆ˜μ • public int update(Users user) throws Exception; // νšŒμ› μ‚­μ œ public int delete(String userId) throws Exception; }
Java
볡사

UserServiceImpl.java

@Slf4j @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Override public int insert(Users user) throws Exception { // λΉ„λ°€λ²ˆν˜Έ μ•”ν˜Έν™” String userPw = user.getUserPw(); String encodedPw = passwordEncoder.encode(userPw); user.setUserPw(encodedPw); // νšŒμ› 등둝 int result = userMapper.insert(user); // κΆŒν•œ 등둝 if( result > 0 ) { UserAuth userAuth = new UserAuth(); userAuth.setUserId( user.getUserId() ); userAuth.setAuth("ROLE_USER"); // κΈ°λ³Έ κΆŒν•œ : μ‚¬μš©μž κΆŒν•œ (ROLE_USER) result = userMapper.insertAuth(userAuth); } return result; } @Override public Users select(int userNo) throws Exception { return userMapper.select(userNo); } @Override public void login(Users user, HttpServletRequest requset) throws Exception { String username = user.getUserId(); String password = user.getUserPwCheck(); log.info("username : " + username); log.info("password : " + password); // 아이디, νŒ¨μŠ€μ›Œλ“œ 인증 토큰 생성 UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); // 토큰에 μš”μ²­μ •λ³΄λ₯Ό 등둝 token.setDetails( new WebAuthenticationDetails(requset) ); // 토큰을 μ΄μš©ν•˜μ—¬ 인증(둜그인) Authentication authentication = authenticationManager.authenticate(token); User authUser = (User) authentication.getPrincipal(); log.info("인증된 μ‚¬μš©μž 아이디 : " + authUser.getUsername()); SecurityContextHolder.getContext().setAuthentication(authentication); } @Override public int update(Users user) throws Exception { // λΉ„λ°€λ²ˆν˜Έ μ•”ν˜Έν™” String userPw = user.getUserPw(); String encodedPw = passwordEncoder.encode(userPw); user.setUserPw(encodedPw); int result = userMapper.update(user); return result; } @Override public int delete(String userId) throws Exception { int result = userMapper.delete(userId); return result; } }
Java
볡사

인증

JWT 토큰 인증

β€’
SecurityConstants.java
β€’
AuthenticationRequest.java
β€’
JwtProps.java
β€’
SecurityConfig.java
β€’
CustomUser.java
β€’
CustomUserDetailService.java

SecurityConstants.java

public final class SecurityConstants { // πŸ”— 둜그인 경둜 public static final String AUTH_LOGIN_URL = "/login"; // 🎫 인증 μš”μ²­ 헀더 // Authorization : "Bearer " + πŸ’(JWT) public static final String TOKEN_HEADER = "Authorization"; public static final String TOKEN_PREFIX = "Bearer "; public static final String TOKEN_TYPE = "JWT"; }
Java
볡사

AuthenticationRequest.java

@Getter @Setter @ToString public class AuthenticationRequest { private String username; private String password; }
Java
볡사

JwtProps.java

@Data @Component @ConfigurationProperties("com.aloha.jwt") // com.aloha.jwt 경둜 ν•˜μœ„ 속성듀을 지정 public class JwtProps { // com.aloha.jwt.secretKey둜 μ§€μ •λœ ν”„λ‘œνΌν‹° 값을 μ£Όμž…λ°›λŠ” ν•„λ“œ // βœ… com.aloha.jwt.secret-key ➑ secretKey : {μΈμ½”λ”©λœ μ‹œν¬λ¦Ώ ν‚€} private String secretKey; }
Java
볡사

SecurityConfig.java

@Slf4j @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) //μ–΄λ…Έν…Œμ΄μ…˜μ— prePostEnabled = trueλ₯Ό μΆ”κ°€ν•˜λ©΄ AuthenticationManagerλ₯Ό μžλ™μœΌλ‘œ κ΅¬μ„±ν•©λ‹ˆλ‹€. public class SecurityConfig { @Autowired private CustomUserDetailService customUserDetailService; @Autowired private JwtTokenProvider jwtTokenProvider; private AuthenticationManager authenticationManager; @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { this.authenticationManager = authenticationConfiguration.getAuthenticationManager(); return authenticationManager; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { log.info("securityFilterChain..."); // 폼 기반 둜그인 λΉ„ν™œμ„±ν™” http.formLogin( login -> login.disable() ); // HTTP κΈ°λ³Έ 인증 λΉ„ν™œμ„±ν™” http.httpBasic( basic -> basic.disable() ); // CSRF(Cross-Site Request Forgery) 곡격 λ°©μ–΄ κΈ°λŠ₯ λΉ„ν™œμ„±ν™” http.csrf( csrf -> csrf.disable() ); // ν•„ν„° μ„€μ • // βœ… JWT μš”μ²­ ν•„ν„° 1️⃣ // βœ… JWT 인증 ν•„ν„° 2️⃣ http.addFilterAt(new JwtAuthenticationFilter(authenticationManager, jwtTokenProvider), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(new JwtRequestFilter(authenticationManager, jwtTokenProvider), UsernamePasswordAuthenticationFilter.class) ; // 인가 μ„€μ • http.authorizeHttpRequests() .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() .antMatchers("/").permitAll() .antMatchers("/login").permitAll() .antMatchers("/user/**").hasAnyRole("USER" , "ADMIN") .antMatchers("/admin/**").hasRole("ADMIN") // .anyRequest().authenticated() ; // μ‚¬μš©μž 정보λ₯Ό λΆˆλŸ¬μ˜€λŠ” μ„œλΉ„μŠ€ μ„€μ • http.userDetailsService(customUserDetailService); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
Java
볡사

CustomUser.java

@Getter @ToString public class CustomUser implements UserDetails { private Users user; public CustomUser(Users user) { this.user = user; } /** * πŸŸ’πŸŸ‘πŸ”΄ κΆŒν•œ getter λ©”μ†Œλ“œ * βœ… UserDetails λ₯Ό CustomUser 둜 κ΅¬ν˜„ν•˜μ—¬, * Spring Security 의 User λŒ€μ‹  μ‚¬μš©μž μ •μ˜ 인증 객체(CustomUser)λ₯Ό μ‚¬μš©ν•œλ‹€λ©΄, * κΆŒν•œμ€ 'ROLE_' λΆ™μ—¬μ„œ μ‚¬μš©ν•΄μ•Όν•œλ‹€. */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { return user.getAuthList().stream() .map( (auth) -> new SimpleGrantedAuthority(auth.getAuth())) .collect(Collectors.toList()); } @Override public String getPassword() { return user.getUserPw(); } @Override public String getUsername() { return user.getUserId(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return user.getEnabled() == 0 ? false : true; } }
Java
볡사

CustomUserDetailService.java

@Slf4j @Service public class CustomUserDetailService implements UserDetailsService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) { log.info("login - loadUserByUsername : " + username); // MyBatisλ₯Ό μ‚¬μš©ν•˜μ—¬ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‚¬μš©μž μ„ΈλΆ€ 정보λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. Users user = userMapper.login(username); if (user == null) { log.info("μ‚¬μš©μž μ—†μŒ..."); throw new UsernameNotFoundException("μ‚¬μš©μžλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€: " + username); } log.info("user :::::"); log.info(user.toString()); // πŸŸ’πŸŸ‘πŸ”΄ CustomUser (➑User) μ‚¬μš© CustomUser customUser = new CustomUser(user); log.info("customuser :::::"); log.info(customUser.toString()); return customUser; } }
Java
볡사

토큰 Provider

β€’
JwtTokenProvider.java
β—¦
토큰 생성
β—¦
토큰 해석
β—¦
토큰 μœ νš¨μ„± 검사

JwtTokenProvider.java

/** * πŸ” JWT 토큰 κ΄€λ ¨ κΈ°λŠ₯을 μ œκ³΅ν•΄μ£ΌλŠ” 클래슀 * βœ… 토큰 생성 * βœ… 토큰 해석 * βœ… 토큰 μœ νš¨μ„± 검사 */ @Slf4j @Component public class JwtTokenProvider { @Autowired private JwtProps jwtProps; @Autowired private UserMapper userMapper; /* * πŸ‘©β€πŸ’Όβž‘πŸ” 토큰 생성 */ public String createToken(int userNo, String userId, List<String> roles) { byte[] signingKey = getSigningKey(); // JWT 토큰 생성 String jwt = Jwts.builder() .signWith(Keys.hmacShaKeyFor(signingKey), Jwts.SIG.HS512) // μ„œλͺ…에 μ‚¬μš©ν•  킀와 μ•Œκ³ λ¦¬μ¦˜ μ„€μ • // .setHeaderParam("typ", SecurityConstants.TOKEN_TYPE) // deprecated (version: before 1.0) .header() // update (version : after 1.0) .add("typ", SecurityConstants.TOKEN_TYPE) // 헀더 μ„€μ • .and() .expiration(new Date(System.currentTimeMillis() + 864000000)) // 토큰 만료 μ‹œκ°„ μ„€μ • (10일) .claim("uno", "" + userNo) // ν΄λ ˆμž„ μ„€μ •: μ‚¬μš©μž 번호 .claim("uid", userId) // ν΄λ ˆμž„ μ„€μ •: μ‚¬μš©μž 아이디 .claim("rol", roles) // ν΄λ ˆμž„ μ„€μ •: κΆŒν•œ .compact(); log.info("jwt : " + jwt); return jwt; } /** * πŸ”βž‘πŸ‘©β€πŸ’Ό 토큰 해석 * * Authorization : Bearer + {jwt} (authHeader) * ➑ jwt μΆ”μΆœ * ➑ UsernamePasswordAuthenticationToken * @param authHeader * @return * @throws Exception */ public UsernamePasswordAuthenticationToken getAuthentication(String authHeader) { if(authHeader == null || authHeader.length() == 0 ) return null; try { // jwt μΆ”μΆœ String jwt = authHeader.replace("Bearer ", ""); // πŸ”βž‘πŸ‘©β€πŸ’Ό JWT νŒŒμ‹± Jws<Claims> parsedToken = Jwts.parser() .verifyWith(getShaKey()) .build() .parseSignedClaims(jwt); log.info("parsedToken : " + parsedToken); // 인증된 μ‚¬μš©μž 번호 String userNo = parsedToken.getPayload().get("uno").toString(); int no = ( userNo == null ? 0 : Integer.parseInt(userNo) ); log.info("userNo : " + userNo); // 인증된 μ‚¬μš©μž 아이디 String userId = parsedToken.getPayload().get("uid").toString(); log.info("userId : " + userId); // 인증된 μ‚¬μš©μž κΆŒν•œ Claims claims = parsedToken.getPayload(); Object roles = claims.get("rol"); log.info("roles : " + roles); // 토큰에 userId μžˆλŠ”μ§€ 확인 if( userId == null || userId.length() == 0 ) return null; Users user = new Users(); user.setNo(no); user.setUserId(userId); // OK: κΆŒν•œλ„ λ°”λ‘œ Users 객체에 담아보기 List<UserAuth> authList = ((List<?>) roles ) .stream() .map(auth -> new UserAuth(userId, auth.toString()) ) .collect( Collectors.toList() ); user.setAuthList(authList); // OK // CustomeUser 에 κΆŒν•œ λ‹΄κΈ° List<SimpleGrantedAuthority> authorities = ((List<?>) roles ) .stream() .map(auth -> new SimpleGrantedAuthority( (String) auth )) .collect( Collectors.toList() ); // 토큰 μœ νš¨ν•˜λ©΄ // name, email 도 λ‹΄μ•„μ£ΌκΈ° try { Users userInfo = userMapper.select(no); if( userInfo != null ) { user.setName(userInfo.getName()); user.setEmail(userInfo.getEmail()); } } catch (Exception e) { log.error(e.getMessage()); log.error("토큰 유효 -> DB μΆ”κ°€ 정보 μ‘°νšŒμ‹œ μ—λŸ¬ λ°œμƒ..."); } UserDetails userDetails = new CustomUser(user); // OK // new UsernamePasswordAuthenticationToken( μ‚¬μš©μžμ •λ³΄κ°μ²΄, λΉ„λ°€λ²ˆν˜Έ, μ‚¬μš©μžμ˜ κΆŒν•œ(λͺ©λ‘) ); return new UsernamePasswordAuthenticationToken(userDetails, null, authorities); } catch (ExpiredJwtException exception) { log.warn("Request to parse expired JWT : {} failed : {}", authHeader, exception.getMessage()); } catch (UnsupportedJwtException exception) { log.warn("Request to parse unsupported JWT : {} failed : {}", authHeader, exception.getMessage()); } catch (MalformedJwtException exception) { log.warn("Request to parse invalid JWT : {} failed : {}", authHeader, exception.getMessage()); } catch (IllegalArgumentException exception) { log.warn("Request to parse empty or null JWT : {} failed : {}", authHeader, exception.getMessage()); } return null; } // /** * πŸ”β“ 토큰 μœ νš¨μ„± 검사 * @param jwt * @return * β­• true : 유효 * ❌ false : 만료 */ public boolean validateToken(String jwt) { try { // πŸ”βž‘πŸ‘©β€πŸ’Ό JWT νŒŒμ‹± Jws<Claims> claims = Jwts.parser() .verifyWith(getShaKey()) .build() .parseSignedClaims(jwt); log.info("::::: 토큰 λ§Œλ£ŒκΈ°κ°„ :::::"); log.info("-> " + claims.getPayload().getExpiration()); /* PAYLOAD { "exp": 1703140095, β¬… λ§Œλ£ŒκΈ°ν•œ μΆ”μΆœ "uid": "joeun", "rol": [ "ROLE_USER" ] } */ return !claims.getPayload().getExpiration().before(new Date()); } catch (ExpiredJwtException exception) { log.error("Token Expired"); // 토큰 만료 return false; } catch (JwtException exception) { log.error("Token Tampered"); // 토큰 손상 return false; } catch (NullPointerException exception) { log.error("Token is null"); // 토큰 μ—†μŒ return false; } catch (Exception e) { return false; } } // secretKey ➑ signingKey private byte[] getSigningKey() { return jwtProps.getSecretKey().getBytes(); } // secretKey ➑ (HMAC-SHA algorithms) ➑ signingKey private SecretKey getShaKey() { return Keys.hmacShaKeyFor(getSigningKey()); } }
Java
볡사

ν•„ν„° μ„€μ •

β€’
JWT 토큰 인증 ν•„ν„°
β—¦
JwtAuthenticationFilter.java
β€’
JWT 토큰 μš”μ²­ ν•„ν„°
β—¦
JwtRequestFilter.java

JwtAuthenticationFilter.java

@Slf4j public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; // μƒμ„±μž public JwtAuthenticationFilter( AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider ) { this.authenticationManager = authenticationManager; this.jwtTokenProvider = jwtTokenProvider; // πŸ”— ν•„ν„° URL 경둜 μ„€μ • : /login setFilterProcessesUrl(SecurityConstants.AUTH_LOGIN_URL); } /** * πŸ” 인증 μ‹œλ„ λ©”μ†Œλ“œ * : /login 경둜둜 (username, password) λ₯Ό μš”μ²­ν•˜λ©΄ 이 ν•„ν„°μ—μ„œ κ±Έλ € 인증을 μ‹œλ„ν•©λ‹ˆλ‹€. * βœ… Authentication 인증 μ‹œλ„ν•œ μ‚¬μš©μž 인증 객체λ₯Ό λ°˜ν™˜ν•˜μ—¬, μ‹œνλ¦¬ν‹°κ°€ 인증 성곡 μ—¬λΆ€λ₯Ό νŒλ‹¨ν•˜κ²Œ ν•©λ‹ˆλ‹€. * @param request * @param response * @return * @throws AuthenticationException */ @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { String username = request.getParameter("username"); String password = request.getParameter("password"); log.info("username : " + username); log.info("password : " + password); // μ‚¬μš©μž 인증정보 객체 생성 Authentication authentication = new UsernamePasswordAuthenticationToken(username, password); // μ‚¬μš©μž 인증 (둜그인) authentication = authenticationManager.authenticate(authentication); /* πŸ” authenticate() 인증 처리 ν”„λ‘œμ„ΈμŠ€ 1️⃣ 주어진 Authentication κ°μ²΄μ—μ„œ μ‚¬μš©μžμ˜ 아이디λ₯Ό μΆ”μΆœν•©λ‹ˆλ‹€. 2️⃣ UserDetailsServiceλ₯Ό μ‚¬μš©ν•˜μ—¬ ν•΄λ‹Ή 아이디에 λŒ€ν•œ UserDetails 객체λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. 3️⃣ κ°€μ Έμ˜¨ UserDetails κ°μ²΄μ—μ„œ μ €μž₯된 λΉ„λ°€λ²ˆν˜Έλ₯Ό ν™•μΈν•˜κΈ° μœ„ν•΄ PasswordEncoderλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. 4️⃣ μ‚¬μš©μžκ°€ μ œκ³΅ν•œ λΉ„λ°€λ²ˆν˜Έμ™€ μ €μž₯된 λΉ„λ°€λ²ˆν˜Έκ°€ μΌμΉ˜ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. 5️⃣ 인증이 μ„±κ³΅ν•˜λ©΄, μƒˆλ‘œμš΄ Authentication 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•©λ‹ˆλ‹€. βœ… 인증 μ—¬λΆ€λ₯Ό, isAuthenticated() ➑ true 둜 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. */ log.info("authenticationManager : " + authenticationManager); log.info("authentication : " + authentication); log.info("인증 μ—¬λΆ€(isAuthenticated) : " + authentication.isAuthenticated()); // 인증 μ‹€νŒ¨ (username, password 뢈일치) if( !authentication.isAuthenticated() ) { log.info("인증 μ‹€νŒ¨ : 아이디와 λΉ„λ°€λ²ˆν˜Έκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."); response.setStatus(401); } return authentication; } /** * β­• 인증 성곡 λ©”μ†Œλ“œ * : attemptAuthentication() 호좜 ν›„, λ°˜ν™˜λœ Authentication - μ‚¬μš©μž 인증 객체가 인증된 것이 ν™•μΈλ˜λ©΄, ν˜ΈμΆœλ©λ‹ˆλ‹€. * * ➑ πŸ” JWT * : 둜그인 인증에 μ„±κ³΅ν–ˆμœΌλ―€λ‘œ, JWT 토큰을 μƒμ„±ν•˜μ—¬ * 응닡(response) 헀더에 jwt 토큰을 λ‹΄μ•„ μ‘λ‹΅ν•©λ‹ˆλ‹€. * πŸ’ { Authorization : Bearer + {jwt} } * @param request * @param response * @param chain * @param authentication * @throws IOException * @throws ServletException */ @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException { log.info("인증 성곡 (auth SUCCESS) : "); CustomUser user = ((CustomUser) authentication.getPrincipal()); int userNo = user.getUser().getNo(); String userId = user.getUser().getUserId(); List<String> roles = user.getAuthorities() .stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.toList()); // πŸ” JWT String token = jwtTokenProvider.createToken(userNo, userId, roles); // πŸ’ { Authorization : Bearer + {jwt} } response.addHeader(SecurityConstants.TOKEN_HEADER, SecurityConstants.TOKEN_PREFIX + token); response.setStatus(200); } }
Java
볡사

JwtRequestFilter.java

@Slf4j public class JwtRequestFilter extends OncePerRequestFilter { private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; // μƒμ„±μž public JwtRequestFilter( AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider ) { this.authenticationManager = authenticationManager; this.jwtTokenProvider = jwtTokenProvider; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // HTTP ν—€λ”μ—μ„œ 토큰을 κ°€μ Έμ˜΄ String header = request.getHeader(SecurityConstants.TOKEN_HEADER); log.info("authorization : " + header); //βœ… Bearer + {jwt} 체크 // 헀더가 μ—†κ±°λ‚˜ ν˜•μ‹μ΄ μ˜¬λ°”λ₯΄μ§€ μ•ŠμœΌλ©΄ λ‹€μŒ ν•„ν„°λ‘œ 진행 if (header == null || header.length() == 0 || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) { filterChain.doFilter(request, response); return; } // πŸ” JWT // Bearer + ${jwt} ➑ "Bearer " 제거 String jwt = header.replace(SecurityConstants.TOKEN_PREFIX, ""); // 토큰을 μ‚¬μš©ν•˜μ—¬ Authentication 객체 생성 Authentication authentication = jwtTokenProvider.getAuthentication(jwt); // 토큰 유효 검사 (토큰이 λ§Œλ£Œλ˜μ§€ μ•Šμ•˜μœΌλ©΄) if( jwtTokenProvider.validateToken(jwt) ) { log.info("μœ νš¨ν•œ JWT ν† ν°μž…λ‹ˆλ‹€."); // πŸ‘©β€πŸ’Ό [둜그인] // SecurityContextHolder(μ‚¬μš©μž λ³΄μ•ˆμ •λ³΄λ₯Ό λ‹΄λŠ” 객체)에 // Authentication(μ‚¬μš©μž 인증 정보) 객체λ₯Ό μ„€μ • SecurityContextHolder.getContext().setAuthentication(authentication); } // λ‹€μŒ ν•„ν„°λ‘œ 진행 filterChain.doFilter(request, response); } }
Java
볡사