feat(user-service): Spring Security 및 JWT 로그인 인증, 인가 구현

- Spring Security 구현
- 로그인 인증 구현
- 인증 성공 시 JWT 발급 구현
This commit is contained in:
bum12ark
2022-02-15 16:19:10 +09:00
parent 285ca1cd25
commit 72c52e4b58
5 changed files with 187 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
package com.justpickup.userservice.global.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class AppConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,10 @@
package com.justpickup.userservice.global.dto;
import lombok.Data;
@Data
public class LoginRequest {
private String name;
private String email;
private String password;
}

View File

@@ -0,0 +1,28 @@
package com.justpickup.userservice.global.security;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
public class HeaderAuthorizationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (request.getServletPath().equals("/login")) {
filterChain.doFilter(request, response);
return;
}
String email = request.getHeader("jwt-sub");
log.info("email jwt-sub = {}", email);
filterChain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,91 @@
package com.justpickup.userservice.global.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.justpickup.userservice.global.dto.LoginRequest;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.stream.Collectors;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RequiredArgsConstructor
@Slf4j
public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
// login 리퀘스트 패스로 오는 요청을 판단
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
Authentication authentication;
try {
LoginRequest credential = new ObjectMapper().readValue(request.getInputStream(), LoginRequest.class);
authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(credential.getEmail(), credential.getPassword())
);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return authentication;
}
// 로그인 성공 이후 토큰 생성
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
org.springframework.security.core.userdetails.User user = (User) authResult.getPrincipal();
String accessToken = Jwts.builder()
.setSubject(user.getUsername())
.setExpiration(
new Date(System.currentTimeMillis() + 10 * 60 * 1000)
)
.signWith(SignatureAlgorithm.HS512, "your-256-bit-secret")
.setIssuer(request.getRequestURI())
.addClaims(Map.of("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList())))
.compact();
String refreshToken = Jwts.builder()
.setSubject(user.getUsername())
.setExpiration(
new Date(System.currentTimeMillis() + 30 * 60 * 1000)
)
.signWith(SignatureAlgorithm.HS512, "your-256-bit-secret")
.setIssuer(request.getRequestURI())
.compact();
Map<String, String> tokens = Map.of(
"access_token", accessToken,
"refresh_token", refreshToken
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
}
@Override
protected void unsuccessfulAuthentication
(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
log.warn("로그인 실패!!");
}
}

View File

@@ -0,0 +1,43 @@
package com.justpickup.userservice.global.security;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
LoginAuthenticationFilter loginAuthenticationFilter = new LoginAuthenticationFilter(authenticationManagerBean());
loginAuthenticationFilter.setFilterProcessesUrl("/login");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/login").permitAll();
http.addFilter(loginAuthenticationFilter);
http.addFilterBefore(new HeaderAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}