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
๋ณต์‚ฌ