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