참고 사이트 : https://imbf.github.io/spring/2020/06/29/Spring-Security-with-JWT.html
Spring Security + JWT를 통해 프로젝트에 인증 구현하기
Spring Security와 JWT를 활용해서 프로젝트에 인증을 어떻게 구현했는지에 대해서 포스팅 하려고 한다.
imbf.github.io
Spring Security란?
Spring Security란 인증과, 권한부여, 일반적인 공격에 대한 보호의 기능을 제공하는 프레임워크이다. 즉, Spring Security를 사용하면 어플리케이션의 보안 관련 기능을 자체적으로 구현 할 필요 없이 쉽고 안전하게 구현할 수 있다.
Spring Security의 Servlet 보안 지원은 Servlet Filter(이하 Filter)를 기반으로 한다. 클라이언트가 어플리케이션으로 request를 보내면, Servlet Container는 요청 URI의 경로에 따라 어떤 Filter와 어떤 Servlet을 적용할 것인지 결정한다.
Spring은 여러 Filter중 Servlet Container Lifecycle과 ApplicationContext사이에 연결할 수 있는 DelegatingFilterProxy라는 Filter를 제공한다.
Spring Security의 Servlet Filter 지원은 DelegatingFilterProxy가 감싸고 있는 FilterChainProxy에 의해 수행되며, FilterChainproxy는 Security Filter Chain을 통해 많은 작업을 Security Filter 인스턴스에 위임한다.
위 과정을 그림으로 나타내면 다음과 같다.

Security Filter Chain은 스프링에서 보안과 관련된 여러 Security Filter List를 갖고 있는 객체로 이를 순회하면서 필터링을 실시한다.
SecurityFilterChain에 존재하는 Security Filter순서는 다음과 같다.
- ChannelProcessingFilter
- ConcurrentSessionFilter
- WebAsyncManageIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- CsrfFilter
- LogoutFilter
- OAuth2AuthorizationRequestRedirectFilter
- Saml2WebSsoAuthenticationRequestFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- Saml2WebSsoAuthenticationFilter
- UsernamePasswordAuthenticationFilter
- ConcurrentSessionFilter
… (총 33개의 Spring Security Filter가 존재한다.)
위의 Security Filter중 Security Authentication Filter(UsernamePasswordAuthentication, OAuth2LoginAuthenticationFilter와 같은 인증 필터)는 AuthenticationManager를 통해 인증을 수행한다.
AuthenticationManager는 Spring Security의 필터들이 인증을 수행하는 방법에 대한 명세를 정의해 놓은 인터페이스이다.
이 AuthenticationManager는 일반적으로 ProviderManager로 구현되며, ProviderManager는 여러 AuthenticationProvider에 인증을 위임한다.
여러 AuthenticationProvider중 하나라도 인증에 성공한다면 ProviderManager에게 인증된 Authentication객체를 반환하고 이는 event 기반으로 AuthenticationFilter에 전송된다.
ProviderManager에 설정된 AuthenticationProvider중 어느 것도 성공적으로 인증을 수행할 수 없다면, 인증은 실패할 것이고 알맞는 예외가 ProviderManager에게 건내질 것이다.
인증이 성공할 경우 AuthenticationFilter는 SecuritycontextHolder의 SecurityContext에 인증된 Authentication 객체를 저장할 수 있도록 한다.
아래의 그림은 Username and Password 인증 방식의 아키텍처이다.

위 그림의 AuthenticationFilter의 역할은 UsernamePasswordAuthenticationFilter가 수행하고 전체적인 프로세스는 다음과 같다.
- Client가 어플리케이션에 요청을 보내면, Servlet Filter에 의해서 Security Filter로 Security 작업이 위임되고 여러 Security Filter 중에서 UsernamePasswordAuthenticationFilter(Username and Password Authentication 방식에서 사용하는 AuthenticationFilter)에서 인증을 처리한다.
- AuthenticationFilter(UsernamePasswordAuthenticationFilter인데 지금부터 AuthenticationFilter라고 부름)는 Servlet 요청 객체(HttpServletRequest)에서 username과 password를 추출해 UsernameAuthenticationToken(이하 인증 객체)을 생성한다.
- AuthenticationFilter는 AuthenticationManager(구현체 : ProviderManager)에게 인증 객체를 전달한다.
- ProviderManager는 인증을 위해 AuthenticationProvider에게 인증 객체를 전달한다.
- AuthenticationProvider는 전달받은 인증 객체의 정보(일반적으로 사용자 아이디)를 UserDetailsService에 넘겨준다.
- UserDetailsService는 전달 받은 사용자 정보를 통해 DB에서 알맞는 사용자를 찾고 이를 기반으로 UserDetails객체를 만듭니다.
- 사용자 정보와 일치하는 UserDetails객체를 AuthenticationProvider에 전달합니다.
- AuthenticationProvider은 전달받은 UserDetails를 인증해 성공하면 ProviderManager에게 권한(Authorities)을 담은 검증된 인증 객체를 전달합니다.
- ProviderManager는 검증된 인증 객체를 AuthenticationFilter에게 전달합니다. (event 기반 으로 전달)
- AuthenticationFilter는 검증된 인증 객체를 SecurityContextHolder의 SecurityContext에 저장합니다.
Spring Security 전체를 설명하기에는 하나의 포스팅으로 부족하기 때문에 아주 기본적인 프로세스만 설명했고, 이제 메인 주제인 나의 프로젝트에 어떻게 Spring Security + JWT를 적용했는지에 대해서 알아보도록 하겠다.
※ back
java 1.8
spring boot 2.5.2
사용된 dependency
- jpa , jjwt 0.9.1 , lombok , spring-security , spring-devtools , spring-web , validation , json
, apache-commons-lang3
※ front
vue2
사용된 dependency
- axios , vee-validate , fontawesome , store , router , vuex , vue-password-strength-meter , zxcvbn
※ database
mysql
1. 사용 할 VO 생성
회원가입시 필요한 User , Role , ERole 을 생성하였다
1-1 . User VO
package com.dong.catShopAPI.models;
import lombok.Data;
import lombok.ToString;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.annotation.CreatedDate;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
@Entity
@Data
@ToString
@Table( name = "users",
uniqueConstraints = {
@UniqueConstraint(columnNames = "email")
})
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userSeq;
@NotBlank
@Size(max = 20)
private String username;
@NotBlank
@Size(max = 50)
@Email
private String email;
@NotBlank
@Size(max = 120)
private String password;
@Size(max = 120)
private String clientId;
@CreatedDate
private LocalDateTime regDt;
@UpdateTimestamp
private LocalDateTime mdfDt;
public User() {
}
public User(String username, String email, String password , String clientId) {
this.username = username;
this.email = email;
this.password = password;
this.clientId = clientId;
}
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable( name = "user_roles",
joinColumns = @JoinColumn(name = "user_seq"),
inverseJoinColumns = @JoinColumn(name = "role_seq"))
private Set<Role> roles = new HashSet<>();
}
1-2 . Role VO
package com.dong.catShopAPI.models;
import lombok.Data;
import javax.persistence.*;
@Entity
@Data
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer roleSeq;
@Enumerated(EnumType.STRING)
@Column(length = 20)
private ERole name;
public Role() {
}
public Role(ERole name) {
this.name = name;
}
}
1-3 . ERole VO
package com.dong.catShopAPI.models;
public enum ERole {
ROLE_USER,
ROLE_MODERATOR,
ROLE_ADMIN
}
2. payload (request , response)
프론트와 넘겨주고 받는거니 프론트와 얘기 후 만들면 되겠다..
3. repository
3-1. RoleRepository
package com.dong.catShopAPI.repository;
import com.dong.catShopAPI.models.ERole;
import com.dong.catShopAPI.models.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface RoleRepository extends JpaRepository<Role,Long> {
Optional<Role> findByName(ERole name);
}
3-2. UserRepository
package com.dong.catShopAPI.repository;
import com.dong.catShopAPI.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User,Long> {
Optional<User> findByEmail(String email);
Boolean existsByEmail(String email);
}
4. security
jwt , service 패키지 를 두개 만들었다
4-1 jwt
1) AuthTokenFilter
클라이언트에서 요청시 제일 먼저 접근 되는 곳이다
토큰 유뮤 파악 후 filterChain.doFilter 메서드로 provider로 보낸다 이 설정 또한 해야하는데 5번에서 작성 할 것이다
토큰이 있다는 가정하에
처음 parseJwt 실행되면 헤더에 있는 토큰 값을 추출 후
doFilterInternal 의 첫 if문에서 있는지 유무와 JwtUtils에 있는 validate 검증 후 리프레시 토큰 필요 유무를 체크하고
사용자 정보를 SecurityContextHolder 에 등록한다.
없을 경우 바로 doFilter 메서드가 실행된다.
package com.dong.catShopAPI.security.jwt;
import com.dong.catShopAPI.security.service.UserDetailsServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AuthTokenFilter extends OncePerRequestFilter {
private AuthTokenProvider authTokenProvider;
private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsServiceImpl userDetailsService;
public AuthTokenFilter (AuthTokenProvider authTokenProvider){
this.authTokenProvider = authTokenProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
// 토큰에서 사용자 이메일 추출
String email = jwtUtils.getUserIdFromJwtToken(jwt);
//사용자 조회 (이메일로 조회 커스텀)
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
// 리프레시 토큰 생성할지 파악
if( jwtUtils.needRefreshToken(jwt , 1000L * 60L) ){ //10분
logger.info("JWT Refresh token USE");
String refreshJwtToken = jwtUtils.createJwtRefreshToken(null , email);
response.setHeader("Authorization" , refreshJwtToken);
}
//사용자 정보 등록
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(userDetails); //new WebAuthenticationDetailsSource().buildDetails(request)
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
2) AuthTokenProvider
실제로 로그인을 할 수 있는지 혹은 여러가지 예외 처리를 하는 곳이다.
컨트롤러에서 authenticate 메서드를 사용하면 지금까지 적용한게 쭈욱 적용된다.
나는 회원 가입할때 비밀번호만 암호화 할 것이며 간단하게 단방향으로 스프링에서 제공되는 PasswordEncoder를
사용할 것이다. 양방향으로 할 경우 비밀번호 복호화 할 수 있어 단방향으로 한다.. 물론 encode 도 추가 작업하면 좋다
처음 파라미터로 받은 사용자 정보를 불러온 후 비밀번호를 먼저 체크한다 이상이 없을 경우
UsernamePasswordAuthenticationToken 으로 리턴한다.
package com.dong.catShopAPI.security.jwt;
import com.dong.catShopAPI.security.service.UserDetailsImpl;
import com.dong.catShopAPI.security.service.UserDetailsServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class AuthTokenProvider implements AuthenticationManager {
private final static Logger log = LoggerFactory.getLogger(AuthTokenProvider.class);
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
PasswordEncoder encoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 아이디로 회원을 조회 했을 때 존재하는 회원인지
// 기타 등등과 적절한 예외 처리
String email = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
UserDetailsImpl userDetails = userDetailsService.loadUserByUsername( email );
Boolean passwordBool = encoder.matches(password , userDetails.getPassword());
if (!passwordBool) throw new AuthenticationServiceException("비밀번호가 일치하지 않습니다.");
return new UsernamePasswordAuthenticationToken(userDetails
, userDetails.getPassword()
, userDetails.getAuthorities());
}
}
3) AuthEntryPointJwt
JWT의 예외 처리를 하는곳이다.
package com.dong.catShopAPI.security.jwt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
logger.error("Unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: " + authException.getMessage());
}
}
4) JwtUtils
마지막으로 JwtUtils 사실 Provider 에서 해도 되는데 따로 분리를 하였다.
메서드들은 주석을 보면 알 것이다.
jwtSecret , jwtExpirationMs , refreshjwtMs 은 properties에 설정했다.
시크릿키 , 토큰만료일 , 리프레시 토큰 만료일 이다.
package com.dong.catShopAPI.security.jwt;
import com.dong.catShopAPI.security.service.UserDetailsImpl;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
@Value("${bezkoder.app.jwtSecret}")
private String jwtSecret;
@Value("${bezkoder.app.jwtExpirationMs}")
private int jwtExpirationMs;
@Value("${bezkoder.app.refreshjwtMs}")
private int refreshjwtMs;
//토큰 생성
public String createJwtToken(Authentication authentication) {
UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
String email = userPrincipal.getEmail();
return Jwts.builder()
.setSubject(email) //토큰 용도
.setIssuedAt(new Date()) // 토큰 발행일
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) // 토큰 만료일
.signWith(SignatureAlgorithm.HS512, jwtSecret) //서명
.compact(); //토큰 생성
}
//리프레시 토큰 생성
public String createJwtRefreshToken(Authentication authentication , String email) {
if(email == null) {
UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
email = userPrincipal.getEmail();
}
return Jwts.builder()
.setSubject(email) //토큰 용도
.setIssuedAt(new Date()) // 토큰 발행일
.setExpiration(new Date((new Date()).getTime() + refreshjwtMs)) //토큰 만료일
.signWith(SignatureAlgorithm.HS256, jwtSecret) //서명
.compact(); //토큰 생성
}
// 토큰에서 사용자 식별자 변환
public String getUserIdFromJwtToken(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
}
// 리프레시 토큰 생성할지 파악
public Boolean needRefreshToken(String token , Long rangeOfRefreshMillis) {
Date date = Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getExpiration();
Long time = date.getTime();
if(time > 0){
Long reTime = time - System.currentTimeMillis();
return reTime < rangeOfRefreshMillis ? true : false;
}
return false;
}
//토큰 유효성 검사
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
logger.error("잘못된 JWT 서명: {}", e.getMessage());
} catch (MalformedJwtException e) {
logger.error("잘못된 JWT 토큰: {}", e.getMessage());
} catch (ExpiredJwtException e) {
logger.error("만료된 JWT: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
logger.error("지원되지 않는 JWT token: {}", e.getMessage());
} catch (IllegalArgumentException e) {
logger.error("JWT claims 문자열이 비어 있습니다: {}", e.getMessage());
}
return false;
}
}
5. securityConfig JWT 설정
spring-security 시작 전 jwt 필터 검증을 먼저 하도록 securityConfig에서 설정하였다
아 원래는 manager에서 custom 하려고 했으나 provider에서 하였다.
방법 차이니 작동 원리만 안다고 하면 상관 없다.
마지막 configure 만 보면 여태 만든것들을 적용한것을 볼 수 있다
package com.dong.catShopAPI.security;
import com.dong.catShopAPI.security.jwt.*;
import com.dong.catShopAPI.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
// securedEnabled = true,
// jsr250Enabled = true,
prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private AuthTokenProvider authTokenProvider;
@Autowired
UserDetailsServiceImpl userDetailsService;
@Autowired
private AuthEntryPointJwt unauthorizedHandler;
/** custom **/
/*
@Bean
public CustomAuthenticationProcessingFilter customAuthenticationProcessingFilter () throws Exception {
CustomAuthenticationProcessingFilter filter = new CustomAuthenticationProcessingFilter("/login");
filter.setAuthenticationManager(customAuthenticationManager());
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler("/login"));
filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/"));
return filter;
}
@Bean
public AuthTokenProvider customAuthenticationManager() throws Exception {
return new AuthTokenProvider();
}
*/
/** custom **/
/*@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}*/
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// cors csrf 미사용
http.cors().and().csrf().disable()
// jwt Exception 핸들링 적용 및 spring security 미사용
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 인증 불필요
.authorizeRequests().antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/auth/admin/**").hasAnyRole("ROLE_ADMIN")
.antMatchers("/api/test/**").permitAll()
// 나머지는 모두 인증
.anyRequest().authenticated();
//스프링 시큐리티 구동전에 커스텀한 jwt Filter 를 먼저 구동
http.addFilterBefore(new AuthTokenFilter(authTokenProvider),UsernamePasswordAuthenticationFilter.class);
//http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
6. controller
마지막으로 실제 사용할 컨트롤러를 만든다
package com.dong.catShopAPI.controller;
import com.dong.catShopAPI.models.ERole;
import com.dong.catShopAPI.models.Role;
import com.dong.catShopAPI.models.User;
import com.dong.catShopAPI.payload.request.LoginRequest;
import com.dong.catShopAPI.payload.request.SignupRequest;
import com.dong.catShopAPI.payload.response.JwtResponse;
import com.dong.catShopAPI.payload.response.MessageResponse;
import com.dong.catShopAPI.repository.RoleRepository;
import com.dong.catShopAPI.repository.UserRepository;
import com.dong.catShopAPI.security.jwt.AuthTokenProvider;
import com.dong.catShopAPI.security.jwt.JwtUtils;
import com.dong.catShopAPI.security.service.UserDetailsImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private static Logger log = LoggerFactory.getLogger(AuthController.class);
@Autowired
AuthTokenProvider authTokenProvider;
@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;
@Autowired
PasswordEncoder encoder;
@Autowired
JwtUtils jwtUtils;
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authTokenProvider.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getEmail(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.createJwtToken(authentication);
String refreshJwt = jwtUtils.createJwtRefreshToken(authentication , null);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(item -> item.getAuthority())
.collect(Collectors.toList());
/*
Cookie cookie = new Cookie("","");
// 7일
cookie.setMaxAge(7 * 24 * 60 * 60);
cookie.setSecure(true);
cookie.setHttpOnly(true);
cookie.setPath("/");
response.addCookie(cookie);
*/
return ResponseEntity.ok(new JwtResponse(jwt, refreshJwt,
userDetails.getUserSeq(),
userDetails.getUsername(),
userDetails.getEmail(),
roles));
}
@PostMapping("/register")
public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByEmail(signUpRequest.getEmail())) {
return ResponseEntity
.badRequest()
.body(new MessageResponse("Error: 이미 사용중인 이메일입니다!" , "300" ));
}
// 사용자 계정 추가
User user = new User(signUpRequest.getUsername(),
signUpRequest.getEmail(),
encoder.encode(signUpRequest.getPassword()),
signUpRequest.getClientId());
Set<String> strRoles = signUpRequest.getRole();
Set<Role> roles = new HashSet<>();
if (strRoles == null) {
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);
} else {
strRoles.forEach(role -> {
switch (role) {
case "admin":
Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(adminRole);
break;
case "mod":
Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(modRole);
break;
default:
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);
}
});
}
user.setRoles(roles);
userRepository.save(user);
return ResponseEntity.ok(new MessageResponse("사용자 등록 성공!" , "200"));
}
@GetMapping("/profile")
public Object profile() {
return SecurityContextHolder.getContext().getAuthentication().getDetails();
}
}
'IT' 카테고리의 다른 글
excel encode & decode 방법 (0) | 2022.03.28 |
---|---|
톰캣 로그 날짜별 생성 설정 - logrotate & 로그 설정별 장단점 (0) | 2022.02.24 |
CENT OS 7 젠킨스 설정 (0) | 2021.04.15 |
MY SQL DB BACK UP(xtrabackup 증분백업) (0) | 2021.04.15 |
AJP (0) | 2021.04.15 |