-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
회원가입 JWT토큰 구현 완료 및 로그인 구현 완료 Related to: #5
- Loading branch information
crayon
committed
Mar 18, 2022
1 parent
83e534a
commit 33c0572
Showing
14 changed files
with
334 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
src/main/java/com/moodstation/springboot/config/JwtAuthenticationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.moodstation.springboot.config; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.web.filter.GenericFilterBean; | ||
|
||
import javax.servlet.FilterChain; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.ServletRequest; | ||
import javax.servlet.ServletResponse; | ||
import javax.servlet.http.HttpServletRequest; | ||
import java.io.IOException; | ||
|
||
@RequiredArgsConstructor | ||
public class JwtAuthenticationFilter extends GenericFilterBean { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
@Override | ||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { | ||
// 헤더에서 JWT 를 받아옵니다. | ||
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request); | ||
// 유효한 토큰인지 확인합니다. | ||
if (token != null && jwtTokenProvider.validateToken(token)) { | ||
// 토큰이 유효하면 토큰으로부터 유저 정보를 받아옵니다. | ||
Authentication authentication = jwtTokenProvider.getAuthentication(token); | ||
// SecurityContext 에 Authentication 객체를 저장합니다. | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} | ||
chain.doFilter(request, response); | ||
} | ||
} | ||
|
73 changes: 73 additions & 0 deletions
73
src/main/java/com/moodstation/springboot/config/JwtTokenProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package com.moodstation.springboot.config; | ||
|
||
import io.jsonwebtoken.*; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UserDetailsService; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.annotation.PostConstruct; | ||
import javax.servlet.http.HttpServletRequest; | ||
import java.util.Base64; | ||
import java.util.Date; | ||
import java.util.List; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class JwtTokenProvider { | ||
|
||
private String secretKey = "moodstationsecret"; | ||
|
||
// 토큰 유효시간 30일 | ||
private long tokenValidTime = 30 * 24 * 60 * 60 * 1000L; | ||
|
||
private final UserDetailsService userDetailsService; | ||
|
||
// 객체 초기화, secretKey를 Base64로 인코딩한다. | ||
@PostConstruct | ||
protected void init() { | ||
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); | ||
} | ||
|
||
// JWT 토큰 생성 | ||
public String createToken(String userPk, List<String> roles) { | ||
Claims claims = Jwts.claims().setSubject(userPk); // JWT payload 에 저장되는 정보단위 | ||
claims.put("roles", roles); // 정보는 key / value 쌍으로 저장된다. | ||
Date now = new Date(); | ||
return Jwts.builder() | ||
.setClaims(claims) // 정보 저장 | ||
.setIssuedAt(now) // 토큰 발행 시간 정보 | ||
.setExpiration(new Date(now.getTime() + tokenValidTime)) // set Expire Time | ||
.signWith(SignatureAlgorithm.HS256, secretKey) // 사용할 암호화 알고리즘과 | ||
// signature 에 들어갈 secret값 세팅 | ||
.compact(); | ||
} | ||
|
||
// JWT 토큰에서 인증 정보 조회 | ||
public Authentication getAuthentication(String token) { | ||
UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUserPk(token)); | ||
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); | ||
} | ||
|
||
// 토큰에서 회원 정보 추출 | ||
public String getUserPk(String token) { | ||
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); | ||
} | ||
|
||
// Request의 Header에서 token 값을 가져옵니다. "X-AUTH-TOKEN" : "TOKEN값' | ||
public String resolveToken(HttpServletRequest request) { | ||
return request.getHeader("ACCESS-TOKEN"); | ||
} | ||
|
||
// 토큰의 유효성 + 만료일자 확인 | ||
public boolean validateToken(String jwtToken) { | ||
try { | ||
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken); | ||
return !claims.getBody().getExpiration().before(new Date()); | ||
} catch (Exception e) { | ||
return false; | ||
} | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
src/main/java/com/moodstation/springboot/config/WebSecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.moodstation.springboot.config; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
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.configuration.WebSecurityConfigurerAdapter; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.crypto.factory.PasswordEncoderFactories; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
@RequiredArgsConstructor | ||
@EnableWebSecurity | ||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
// 암호화에 필요한 PasswordEncoder를 Bean 등록합니다 | ||
@Bean | ||
public PasswordEncoder passwordEncoder() { | ||
return PasswordEncoderFactories.createDelegatingPasswordEncoder(); | ||
} | ||
|
||
// authenticationManager를 Bean 등록합니다. | ||
@Bean | ||
@Override | ||
public AuthenticationManager authenticationManagerBean() throws Exception { | ||
return super.authenticationManagerBean(); | ||
} | ||
|
||
@Override | ||
protected void configure(HttpSecurity http) throws Exception { | ||
http | ||
.httpBasic().disable() // rest api 만을 고려하여 기본 설정은 해제하겠습니다. | ||
.csrf().disable() // csrf 보안 토큰 disable처리. | ||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 토큰 기반 인증이므로 세션 역시 사용하지 않습니다. | ||
.and() | ||
.authorizeRequests() // 요청에 대한 사용권한 체크 | ||
.antMatchers("/admin/**").hasRole("ADMIN") | ||
.antMatchers("/user/**").hasRole("USER") | ||
.anyRequest().permitAll() // 그외 나머지 요청은 누구나 접근 가능 | ||
.and() | ||
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), | ||
UsernamePasswordAuthenticationFilter.class); | ||
// JwtAuthenticationFilter를 UsernamePasswordAuthenticationFilter 전에 넣는다 | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/com/moodstation/springboot/dto/UserLoginRequestDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.moodstation.springboot.dto; | ||
|
||
import com.moodstation.springboot.entity.User; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class UserLoginRequestDto { | ||
|
||
private String email; | ||
private String password; | ||
|
||
@Builder | ||
public UserLoginRequestDto(String email, String password) { | ||
this.email = email; | ||
this.password = password; | ||
} | ||
|
||
public User toEntity() { | ||
return User.builder() | ||
.email(email) | ||
.password(password) | ||
.build(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 48 additions & 27 deletions
75
src/main/java/com/moodstation/springboot/entity/User.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,69 @@ | ||
package com.moodstation.springboot.entity; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.Setter; | ||
import org.springframework.format.annotation.DateTimeFormat; | ||
import lombok.*; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
|
||
import javax.persistence.*; | ||
import java.time.LocalDateTime; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@Getter | ||
@Setter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@Builder | ||
@Entity | ||
@Table(name = "user") | ||
public class User { | ||
public class User implements UserDetails { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@Column(nullable = false) | ||
@Column(length = 100, nullable = false, unique = true) | ||
private String email; | ||
|
||
@Column(nullable = false) | ||
@Column(length = 20, nullable = false, unique = true) | ||
private String nickname; | ||
|
||
@Column(nullable = false) | ||
@Column(length = 30, nullable = false) | ||
private String password; | ||
|
||
@Column(name = "created_at") | ||
@DateTimeFormat(pattern = "yyyy-MM-dd") | ||
private LocalDateTime createdAt; | ||
|
||
@Column(name = "updated_at") | ||
@DateTimeFormat(pattern = "yyyy-MM-dd") | ||
private LocalDateTime updatedAt; | ||
|
||
@Builder | ||
public User(String email, String nickname, String password) { | ||
this.email = email; | ||
this.nickname = nickname; | ||
this.password = password; | ||
this.createdAt = LocalDateTime.now(); | ||
this.updatedAt = LocalDateTime.now(); | ||
@ElementCollection(fetch = FetchType.EAGER) | ||
@Builder.Default | ||
private List<String> roles = new ArrayList<>(); | ||
|
||
@Override | ||
public Collection<? extends GrantedAuthority> getAuthorities() { | ||
return this.roles.stream() | ||
.map(SimpleGrantedAuthority::new) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return email; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonLocked() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isCredentialsNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 10 additions & 14 deletions
24
src/main/java/com/moodstation/springboot/service/UserService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,24 @@ | ||
package com.moodstation.springboot.service; | ||
|
||
import com.moodstation.springboot.dto.UserCreateRequestDto; | ||
import com.moodstation.springboot.dto.UserCreateResponseDto; | ||
import com.moodstation.springboot.dto.UserResponseDto; | ||
import com.moodstation.springboot.entity.User; | ||
import com.moodstation.springboot.repository.UserRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
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; | ||
|
||
import javax.transaction.Transactional; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class UserService { | ||
public class UserService implements UserDetailsService { | ||
|
||
private final UserRepository userRepository; | ||
|
||
// 회원가입 | ||
public String save(UserCreateRequestDto requestDto) { | ||
userRepository.save(User.builder() | ||
.email(requestDto.getEmail()) | ||
.nickname(requestDto.getNickname()) | ||
.password(requestDto.getPassword()) | ||
.build()); | ||
|
||
return "Success"; | ||
@Override | ||
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { | ||
return userRepository.findByEmail(email) | ||
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); | ||
} | ||
} |
Oops, something went wrong.