username / password 이외에 추가 파라미터(ex: OTP 코드)를 처리하기 위한 방법이다.
간단히 요약하면
addFilterBefore 필터에 Custom UsernamePasswordAuthenticationFilter 를 추가하여 Custom WebAuthenticationDetails 로 변경하여 넘겨주는 방식
////////////////////////////////////////////////
// CustomUsernamePasswordAuthenticationFilter.java
////////////////////////////////////////////////
package com.mzc.megatoi.backoffice.web.auth;
import javax.servlet.http.HttpServletRequest;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
@Getter
@Setter
@Component
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
CustomWebAuthenticationDetailsSource customWebAuthenticationDetailsSource;
@Override
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
super.setDetails(request, authRequest);
authRequest.setDetails(customWebAuthenticationDetailsSource.buildDetails(request));
}
}
////////////////////////////////////////////////
// CustomWebAuthenticationDetailsSource.java
////////////////////////////////////////////////
package com.mzc.megatoi.backoffice.web.auth;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
@Component
public class CustomWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new CustomWebAuthenticationDetails(context);
}
}
////////////////////////////////////////////////
// CustomWebAuthenticationDetails.java
////////////////////////////////////////////////
package com.mzc.megatoi.backoffice.web.auth;
import javax.servlet.http.HttpServletRequest;
import lombok.Getter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
@Getter
private String otpSecret;
public CustomWebAuthenticationDetails(HttpServletRequest request) {
super(request);
// request 파라미터에서 추가로 받을 항목
otpSecret = request.getParameter("otpSecret");
}
}
////////////////////////////////////////////////
// UserPassOtpAuthenticationProvider.java
////////////////////////////////////////////////
package com.mzc.megatoi.backoffice.web.auth;
import java.util.List;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.authentication.AuthenticationProvider;
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.authority.AuthorityUtils;
import org.springframework.stereotype.Component;
@Log4j2
@Component
public class UserPassOtpAuthenticationProvider implements AuthenticationProvider {
protected static final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils.createAuthorityList("ADMIN");
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// 아래처럼 추가 항목들을 가져올 수 있음
String otp = ((CustomWebAuthenticationDetails) authentication.getDetails()).getOtpSecret();
// user / pass / otp 체크
// 실패시
// throw new BadCredentialsException("Invalid username/password/otp");
return new UsernamePasswordAuthenticationToken(username, password, ADMIN_ROLES);
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
////////////////////////////////////////////////
// SecurityConfig.java
////////////////////////////////////////////////
public class SecurityConfig extends WebSecurityConfigurerAdapter {
final
UserPassOtpAuthenticationProvider userPassOtpAuthenticationProvider;
@Bean
public CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter() throws Exception {
CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
customUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
return customUsernamePasswordAuthenticationFilter;
}
public SecurityConfig(
this.userPassOtpAuthenticationProvider = userPassOtpAuthenticationProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests();
http.formLogin()
.loginPage(LOGIN_URI)
.successHandler(megatoiAuthenticationSuccessHandler)
.failureHandler(megaToIAuthenticationFailureHandler)
.usernameParameter("username")
.passwordParameter("password")
.permitAll();
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl(LOGIN_URI)
.invalidateHttpSession(true)
.deleteCookies()
.permitAll();
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(this.userPassOtpAuthenticationProvider);
}
}
위 코드는 일부를 발췌한 내용이라 보고 참고 정도만 하세요.