1. pom.xml에 의존성 추가
스프링 시큐리티 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
테스트 코드 작성을 위해서는 spring-security-test 도 필요하다.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2. security관련 package생성 후 config클래스 작성
WebSecurityConfigureAdapter를 상속받아 클래스를 생성
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
- security를 추가하면 자동으로 기본 로그인 페이지가 생성되는데, 그 설정을 취소하고 csrf사용도 취소함
- signIn과 signUp(로그인과 회원가입)에 대한 요청은 모두 허용, 그 외 요청은 인증된 회원만 접근 가능하도록 설정
- exceptionHandling과 addFilterBefore에 대한 클래스들은 아래에서 작성할 것임
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable() // security에서 기본으로 생성하는 login페이지 사용 안 함
.csrf().disable() // csrf 사용 안 함 == REST API 사용하기 때문에
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // JWT인증사용하므로 세션 사용 함
.and()
.authorizeRequests() // 다음 리퀘스트에 대한 사용권한 체크
.antMatchers("/*/signIn", "/*/signUp").permitAll() // 가입 및 인증 주소는 누구나 접근가능
.anyRequest().hasRole("USER") // 그외 나머지 요청은 모두 인증된 회원만 접근 가능
.and()
.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
}
+ swagger를 사용할 경우 추가해주기
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
+ customUserDetails등록
직접 UserDetailsService를 커스텀하게된다면 꼭 등록해주기!
// SecurityConfig.java
@Autowired
private CustomUserDetailService customUserDetailService;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailService);
}
CustomUserDetailService클래스도 작성해주고...
@Service
public class CustomUserDetailService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.getUserInfo(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
} else {
return user;
}
}
}
3. jwt토큰 생성 및 유효성 검증 관련 컴포넌트 클래스 작성
@RequiredArgsConstructor
@Component
public class JwtTokenProvider { // JWT토큰 생성 및 유효성을 검증하는 컴포넌트
}
아래 코드와 같이 작성해주면 되는데
SECRET_KEY는 properties파일에서 불러오는 값
createToken메서드를 통해 JWT 토큰이 생성되고, 이 토큰값을 통해 유저 정보에 접근할 수 있다.
인자값 중 userPk는 user를 식별할 수 있는 값이며, roles는 말그대로 role인데, 이는 userDetails를 상속받아 userDto를 생성해보면 무슨 값을 넘겨주면 되는지 이해할 수 있다. USER, ADMIN같은 값임.
@RequiredArgsConstructor
@Component
public class JwtTokenProvider { // JWT토큰 생성 및 유효성을 검증하는 컴포넌트
@Value("spring.jwt.secret")
private String SECRET_KEY;
private long tokenValidMilisecond = 1000L * 60 * 60; // 1시간만 토큰 유효
private final UserDetailsService userDetailsService;
@PostConstruct
protected void init() {
SECRET_KEY = Base64.getEncoder().encodeToString(SECRET_KEY.getBytes());
}
// Jwt 토큰 생성
public String createToken(String userPk, Collection<? extends GrantedAuthority> roles) {
Claims claims = Jwts.claims().setSubject(userPk);
claims.put("roles", roles);
Date now = new Date();
return Jwts.builder()
.setClaims(claims) // 데이터
.setIssuedAt(now) // 토큰 발행일자
.setExpiration(new Date(now.getTime() + tokenValidMilisecond)) // set Expire Time
.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 암호화 알고리즘, secret값 세팅
.compact();
}
}
만약 CustomUserDetailService했다면 당연히 JwtProvider에서도 사용해야 한다... Autowired 어노테이션 잊지말기...
@Autowired
private CustomUserDetailService customUserDetailsService;
4. CustomAuthenticationEntryPoint클래스 작성
권한이 없을 경우 알려주는.. 401 에러
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
// TODO Auto-generated method stub
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
5. 로그인 해보기!
컨트롤러
/*** 유저 로그인 ***/
@ApiOperation(value="유저 로그인")
@PostMapping(value="/users/signIn")
public ResponseEntity<?> userSignIn(@RequestBody User userInfo) {
Map<String, Object> resultMap = new HashMap<String, Object>();
User loginUser = userService.userSignIn(userInfo);
if(loginUser == null) {
resultMap.put("resCd", "9999");
resultMap.put("resMsg", "로그인 실패");
}else {
resultMap.put("token", loginUser.getU_token());
resultMap.put("resCd", "0000");
resultMap.put("resMsg", "로그인 성공");
}
return new ResponseEntity<>(resultMap, HttpStatus.OK);
}
서비스
public User userSignIn(User userInfo) {
User user = userMapper.userSignIn(userInfo.getU_email());
if(!passwordEncoder.matches(userInfo.getU_password(), user.getU_password())) {
return null;
}else {
String token = jwtTokenProvider.createToken(user.getU_email(), user.getAuthorities());
user.setU_token(token);
return user;
}
}
포스트맨으로 실행해본 결과 토큰값이 잘 도착한다..
'STUDY > Spring' 카테고리의 다른 글
Spring | Illegal overloaded getter method (0) | 2020.10.12 |
---|---|
Spring | JSON 사용하기 ( org.json ) (0) | 2020.10.07 |
Spring | properties 사용하기 (0) | 2020.07.09 |
Spring | 파일 업로드 ( + Spring Security ) (0) | 2020.07.06 |
Spring | 스프링 시큐리티(Spring Security) (4) MyBatis이용 로그인 (1) | 2020.07.02 |