spring securiry登録には認証コードフィルタを追加します.
13418 ワード
暇なので、半月ほどスプリントsecurityを見ました.ログイン時に、より多くのユーザデータを検証する方法を考えています.例えば、認証コードです.
spring security公式文書の第10章で、spring security検証の手順を述べた.
spring securityではデフォルトの検証方法はすべてProvider Managerを呼び出すことによって、authentication Providersを輪講して一つ一つ検証して、検証の目的を達成します.しかし、デフォルトの検証以外の検証はサポートされていません.そこで次のような考えができました.
私達もFilterをカスタマイズして、彼をspring securityの中のfilterChinに追加して、spring secutiryの検証構造によって検証の構造を拡張します.
筆者は以下のコードを、spring securityのUsernamePassword Authentication Filterを参照して実現した.具体的な手順は以下の通りです. は、認証コード認証データ を保存するための認証コードtokenクラスをカスタマイズする.認証失敗異常クラスをカスタマイズします.このクラスはAuthentication Exceptionクラス を継承しなければなりません.は、検証コード検証器 を提供する.カスタム認証マネージャはここでは省略できます.ここではspring securityの構成によって実現されるので、省略されていません. 私たち自身のfilter
ステップは以下の通りです上記の5つのクラスをプロジェクトに追加します. WebSecurityConfigを変更する ログインビューコントローラのコメントを修正します.「login」. 新規登録ビューコントローラ 最後にログインインターフェースに認証コード入力ボックス を追加します.
テストデータのユーザー名は:userパスワード:password検証コード:1000例は比較的簡単で、検証コードは直接に書き込みました.興味のある人は自分で直してもいいです.
この例のソースコード:https://gitee.com/longguiyunjosh/spring-security-demo/tree/master
足りないところを補充してください.もし転載するなら、出典を明記してください.
spring security公式文書の第10章で、spring security検証の手順を述べた.
spring securityではデフォルトの検証方法はすべてProvider Managerを呼び出すことによって、authentication Providersを輪講して一つ一つ検証して、検証の目的を達成します.しかし、デフォルトの検証以外の検証はサポートされていません.そこで次のような考えができました.
私達もFilterをカスタマイズして、彼をspring securityの中のfilterChinに追加して、spring secutiryの検証構造によって検証の構造を拡張します.
筆者は以下のコードを、spring securityのUsernamePassword Authentication Filterを参照して実現した.
public class CodeAuthenticationToken extends AbstractAuthenticationToken{
/**
*
*/
private Object captcha;
/**
*
* true:
* false:
*/
private boolean flag;
public CodeAuthenticationToken() {
super(null);
}
public CodeAuthenticationToken(Object captcha) {
super(null);
this.captcha = captcha;
this.flag = false;
setAuthenticated(false);
}
public CodeAuthenticationToken(Collection extends GrantedAuthority> authorities, Object captcha) {
super(authorities);
this.captcha = captcha;
this.flag = false;
super.setAuthenticated(true); // must use super, as we override
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
public Object getCaptcha() {
return captcha;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
public class CodeAuthenticationException extends AuthenticationException {
public CodeAuthenticationException(String msg, Throwable t) {
super(msg, t);
}
public CodeAuthenticationException(String msg) {
super(msg);
}
}
public class CodeAuthenticationProvider implements AuthenticationProvider {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
logger.debug("custom captcha authentication");
Assert.isInstanceOf(CodeAuthenticationToken.class, authentication," ");
CodeAuthenticationToken custCaptchaToken = (CodeAuthenticationToken) authentication;
String captcha = custCaptchaToken.getCaptcha().toString();
if(captcha.equals("")){
logger.debug(" ");
throw new CodeAuthenticationException(" !");
}
// ,
if(!captcha.equals("1000")){
logger.debug(" ");
throw new CodeAuthenticationException(" !");
}
//
custCaptchaToken.setFlag(true);
return custCaptchaToken;
}
@Override
public boolean supports(Class> authentication) {
return (CodeAuthenticationToken.class.isAssignableFrom(authentication));
}
}
public class CodeAuthenticationManager implements AuthenticationManager {
/**
*
*/
private AuthenticationProvider provider;
public CodeAuthenticationManager(AuthenticationProvider provider) {
Assert.notNull(provider, "provider cannot be null");
this.provider = provider;
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
return this.provider.authenticate(authentication);
}
}
public class CodeFilter extends GenericFilterBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
//
private static final String CODE_ANT_URL = "/login";
private static final String SPRING_SECURITY_FORM_CAPTCHA_KEY = "code";
private String captchaParameter = SPRING_SECURITY_FORM_CAPTCHA_KEY;
private boolean postOnly = true;
//
private RequestMatcher requiresAuthenticationRequestMatcher;
private RememberMeServices rememberMeServices = new NullRememberMeServices();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(CODE_ANT_URL); //
public CodeFilter() {
this.requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(CODE_ANT_URL, "POST");
}
public CodeFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
Assert.notNull(requiresAuthenticationRequestMatcher,"requiresAuthenticationRequestMatcher cannot be null");
this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// ,
if (!requiresAuthentication(request, response)) {
filterChain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
authResult = this.attemptAuthentication(request, response);
if (authResult == null) {
logger.error("Authentication is null!");
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
} catch (InternalAuthenticationServiceException failed) {
logger.error("An internal error occurred while trying to authenticate the user.",failed);
return;
} catch (AuthenticationException failed) {
logger.error("Authentication failed.", failed);
//Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// ,
filterChain.doFilter(request, response);
}
private Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
//
String captcha = request.getParameter(captchaParameter);
if (captcha == null) {
captcha = "";
}
captcha = captcha.trim();
CodeAuthenticationToken authRequest = new CodeAuthenticationToken(captcha);
// , provider
CodeAuthenticationManager manager = new CodeAuthenticationManager(new CodeAuthenticationProvider());
return manager.authenticate(authRequest);
}
/**
*
*
* @param request
* @param response
* @return
*/
protected boolean requiresAuthentication(HttpServletRequest request,
HttpServletResponse response) {
return requiresAuthenticationRequestMatcher.matches(request);
}
/**
*
* {@link org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter}
* Default behaviour for unsuccessful authentication.
*
* - Clears the {@link SecurityContextHolder}
* - Stores the exception in the session (if it exists or
* allowSesssionCreation is set to true)
* - Informs the configured RememberMeServices of the failed login
* - Delegates additional behaviour to the {@link AuthenticationFailureHandler}.
*
*/
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
SecurityContextHolder.clearContext();
if (logger.isDebugEnabled()) {
logger.debug("Authentication request failed: " + failed.toString(), failed);
logger.debug("Updated SecurityContextHolder to contain null Authentication");
logger.debug("Delegating to authentication failure handler " + failureHandler);
}
rememberMeServices.loginFail(request, response);
failureHandler.onAuthenticationFailure(request, response, failed);
}
}
以上は自分で実現する必要があります.以下の筆者はspring bootでspring securityの中のdemoを統合して、検証コード登録を実現します.ステップは以下の通りです
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getFilter(), UsernamePasswordAuthenticationFilter.class) //
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
//
@Bean
public CodeFilter getFilter(){
return new CodeFilter();
}
}
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
registry.addViewController("/hello").setViewName("hello");
//registry.addViewController("/login").setViewName("login");
}
}
@Controller
@RequestMapping("/")
public class LoginController {
//
private static final String LAST_EXCEPTION_KEY = "SPRING_SECURITY_LAST_EXCEPTION";
@RequestMapping("/login")
public String login(HttpServletRequest request, HttpSession session){
//spring security session 。
AuthenticationException authenticationException = (AuthenticationException) session.getAttribute(LAST_EXCEPTION_KEY);
//
if(authenticationException != null && authenticationException instanceof CodeAuthenticationException){
CodeAuthenticationException c = (CodeAuthenticationException) authenticationException;
// , request 。
request.setAttribute("codeError",true);
}
return "login";
}
}
Spring Security Example
Invalid username and password.
You have been logged out.
!
本論文の内容はspring boot 1.5.10とspring security 4.2.4に基づいて実現されるspring security登録検証コードを追加し、既存の検証メカニズムの場合、検証コード検証メカニズムを追加した.テストデータのユーザー名は:userパスワード:password検証コード:1000例は比較的簡単で、検証コードは直接に書き込みました.興味のある人は自分で直してもいいです.
この例のソースコード:https://gitee.com/longguiyunjosh/spring-security-demo/tree/master
足りないところを補充してください.もし転載するなら、出典を明記してください.