728x90
반응형
- Spring Security 를 이용하여 (ajax)비동기 로그인 구현하기
SecurityConfig.java
@Configuration
@EnableWebSecurity
@Order(0)
@Slf4j
public class AjaxSecurityConfig extends WebSecurityConfigurerAdapter {
private String[] permitAllResources = {"/api/public/**","/sample","/aws/**","/sample/**", "/login/**", "/outer/**", "/fonts/**", "/landing/**", "/error/**", "/aws/health/check"};
@Override
public void configure(WebSecurity web) throws Exception {
// 정적 파일은 보안 필터 거치지 않음
web.ignoring()
.antMatchers(permitAllResources)
.requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Bean
private AuthenticationProvider authenticationProvider() {
return new CustomAuthenticationProvider();
}
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(final HttpSecurity http) throws Exception { // username, password
http
.authorizeRequests()
.anyRequest().authenticated();
http
.addFilterBefore(ajaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class); // ajax 로그인
http
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
;
http
.csrf().disable(); // post 방식일 때는 필수
}
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public AjaxLoginProcessingFilter ajaxLoginProcessingFilter() throws Exception {
AjaxLoginProcessingFilter ajaxLoginProcessingFilter = new AjaxLoginProcessingFilter();
ajaxLoginProcessingFilter.setAuthenticationManager(authenticationManager());
ajaxLoginProcessingFilter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler());
ajaxLoginProcessingFilter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler());
return ajaxLoginProcessingFilter;
}
@Bean
public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler(){
return new AjaxAuthenticationSuccessHandler();
}
@Bean
public AuthenticationFailureHandler ajaxAuthenticationFailureHandler(){
return new AjaxAuthenticationFailureHandler();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
AccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler();
((CustomAccessDeniedHandler) accessDeniedHandler).setErrorPage("/denied");
return accessDeniedHandler;
}
}
UsernamePasswordAuthenticationFilter.class 호출 전 ajaxLoginProcessingFilter() 먼저 체크
http
.addFilterBefore(ajaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class); // ajax 로그인
AjaxLoginProcessingFilter.java
public class AjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
public AjaxLoginProcessingFilter(){
super(new AntPathRequestMatcher("/api/login","POST"));
}
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if(!isAjax(request)){
throw new IllegalStateException("Ajax 요청이 아님");
}
Member member = objectMapper.readValue(request.getReader(), Member.class);
if(StringUtils.isEmpty(member.getUserId()) || StringUtils.isEmpty(member.getPassword())){
throw new IllegalStateException("아이디 또는 비밀번호가 존재하지 않습니다.");
}
AjaxAuthenticationToken ajaxAuthenticationToken =
new AjaxAuthenticationToken(member.getUserId(), member.getPassword());
return getAuthenticationManager().authenticate(ajaxAuthenticationToken);
}
private boolean isAjax(HttpServletRequest request){
if("XMLHttpRequest".equals(request.getHeader("X-Requested-with"))){
return true;
}
return false;
}
}
Ajax용 로그인 토큰
/**
* Ajax 로그인용 토큰
*/
public class AjaxAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal;
private Object credentials;
// 인증을 받기전 사용자가 입력한 아이디 비번 담는 생성자
public AjaxAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
// 인증 이후 결과를 담는 생성자
public AjaxAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
}
인증 처리
public class AjaxAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 인증에 관련된 검증 처리
* @param authentication the authentication request object.
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 사용자가 입력한 정보
String username = authentication.getName();
String password = (String)authentication.getCredentials();
// DB에서 가져온 정보
AccountContext accountContext = (AccountContext)userDetailsService.loadUserByUsername(username);
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
// 비밀번호 일치하지 않음
if(!passwordEncoder.matches(password, accountContext.getMember().getPassword())){
throw new BadCredentialsException("BadCredentialsException");
}
// 정책에 따라 추가적인 검증 가능
// 최종적인 인증 객체
return new AjaxAuthenticationToken(accountContext.getMember(), null, accountContext.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
// 토큰 타입과 파라미터 타입이 일치하면 인증 처리.
return authentication.equals(AjaxAuthenticationToken.class);
}
}
/**
* 로그인 성공 시 반환 Json
*/
public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Member member = (Member)authentication.getPrincipal();
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
objectMapper.writeValue(response.getWriter(),member);
}
}
/**
* 로그인 실패 시 반환 Json
*/
public class AjaxAuthenticationFailureHandler implements AuthenticationFailureHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
String errMsg = "로그인 실패";
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// 인증에서 온 에러
if(exception instanceof BadCredentialsException){
errMsg = "Invalid Username or Password";
}else if(exception instanceof InsufficientAuthenticationException){
errMsg = "Invalid Secret Key";
}
objectMapper.writeValue(response.getWriter(), errMsg);
}
}
728x90
반응형
'Spring > Spring Security' 카테고리의 다른 글
[Spring Security] JWT Token (0) | 2023.05.20 |
---|---|
[Spring Security] GET 로그아웃 처리 (0) | 2022.11.03 |
[Spring Security] 스프링 시큐리티를 이용하여 로그인, 회원가입 구현하기 (0) | 2022.10.30 |
[Spring Secururity ERROR] 스프링 빈 순환 참조 에러 The dependencies of some of the beans i (1) | 2022.10.30 |
[Spring Security] Spring Security 설정하기 (0) | 2022.10.29 |