SpringSecurity(四)自定义失败与成功处理器

一、新建MyAuthenticationSuccessHandler继承SavedRequestAwareAuthenticationSuccessHandler

@Component
//自定义登陆成功与失败处理
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {


    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        System.out.println("自定义成功处理逻辑--------------------------");

        //创建一个SecurityContext对象,并且设置上一步得到的Authentication
        SecurityContextHolder.getContext().setAuthentication(authentication);
        HttpSession session = request.getSession();

        //将上一部得到的SecurityContext对象放入session中。到此,自定义用户信息的处理已经完成
        session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
        System.out.println("--------------结束认证");
        System.out.println("将用户信息存储在session中");

        super.setDefaultTargetUrl("/index"); // 设置默认登陆成功的跳转地址
        super.onAuthenticationSuccess(request, response, authentication);


//        RequestCache requestCache = new HttpSessionRequestCache();
//        DefaultSavedRequest savedRequest = (DefaultSavedRequest)requestCache.getRequest(request, response);
//        if (savedRequest == null) {
//            super.onAuthenticationSuccess(request, response, authentication);
//        } else {
//            String targetUrlParameter = this.getTargetUrlParameter();
//            if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
//                this.clearAuthenticationAttributes(request);
//                String targetUrl = savedRequest.getRedirectUrl();
//                this.logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
//                this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
//            } else {
//                requestCache.removeRequest(request, response);
//                super.onAuthenticationSuccess(request, response, authentication);
//            }
//        }
    }

}

DefaultSavedRequest 封装了我们请求的所有信息。RedirectStrategy负责重定向我们的请求。我们可以根据自己的需求来改变注释掉的部分。

二、新建MyAuthenticationFailHandler继承SimpleUrlAuthenticationFailureHandler

package com.springsecurity.handler;

import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;



@Component
public class MyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {


    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        org.slf4j.Logger log = LoggerFactory.getLogger(this.getClass());
        log.info("登录失败:" + exception.getMessage());
        log.info("username=>" + request.getParameter("username"));


        super.onAuthenticationFailure(request, response, exception);


//        if (this.defaultFailureUrl == null) {
//            this.logger.debug("No failure URL set, sending 401 Unauthorized error");
//            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
//        } else {
//            this.saveException(request, exception);
//            if (this.forwardToDestination) {
//                this.logger.debug("Forwarding to " + this.defaultFailureUrl);
//                request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
//            } else {
//                this.logger.debug("Redirecting to " + this.defaultFailureUrl);
//                this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
//            }
//        }

    }
}

同样,根据实际请求,适当的改注释里面得一部分。完成自己的需要

可能有些人对这两段注释掉的不是很熟悉。其实很简单。说的直白一点
这里只是处理页面跳转的 相当于原生servlet里面我们处理页面跳转的那样
request.getRequestDispatcher(“s”).forward(request,response);
response.sendRedirect(“s”);

对于自己处理不了,或者不知道页面转发怎么处理,则调用super.xxx()交给相对应的父类去处理就好

三、创建异常类,用来在处理失败之后,让代码可以走我们自定义的失败处理器,而不是抛一个异常就完事

package com.springsecurity.handler;

import org.springframework.security.core.AuthenticationException;

public class PassWordNotMathException extends AuthenticationException {
    public PassWordNotMathException(String msg, Throwable t) {
        super(msg, t);
    }

    public PassWordNotMathException(String msg) {
        super(msg);
    }
}

这个异常类必须是AuthenticationException 的子类!!!

四、修改SpeingSecurityConfig

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailService myUserDetailService;

    @Autowired
    private MyAuthenticationFailHandler myAuthenticationFailHandler;

    @Qualifier("myAuthenticationProvider")
    @Autowired
    private AuthenticationProvider myAuthenticationProvider;

    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/","/login.html","/testlogin","/mylogin") // 不需要登录就可以访问
                .permitAll()
                .antMatchers("/user/**").hasAnyRole("user") // 需要具有ROLE_USER角色才能访问
                .antMatchers("/admin/**").hasAnyRole("admin") // 需要具有ROLE_ADMIN角色才能访问
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/testlogin") // 设置登录页面
                .loginProcessingUrl("/mylogin")
                //.defaultSuccessUrl("/index") // 设置默认登录成功后跳转的页面
                .successHandler(myAuthenticationSuccessHandler)
                .failureHandler(myAuthenticationFailHandler)
                .and()
                .csrf().disable()// 禁用跨站攻击  关闭csrf 防止循环定向
        ;
    }

    // 密码加密方式
    @Bean("bCryptPasswordEncoder")
    public PasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
    // 重写方法,自定义用户
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(myAuthenticationProvider);
        //auth.userDetailsService(myUserDetailService);
    }

    @Bean("myAuthenticationProvider")
    public AuthenticationProvider getMyAuthenticationProvider(){
        MyAuthenticationProvider myAuthenticationProvider = new MyAuthenticationProvider();
        myAuthenticationProvider.setUserDetailsService(myUserDetailService);
        return myAuthenticationProvider;
    }
}

大概总结下验证的流程
1 、UsernamePasswordAuthenticationFilter.doFilter()
1 方法里面调用attemptAuthentication()。在此方法内
封装UsernamePasswordAuthenticationToken对象然后调用 2

2、AuthenticationManager.authenticate()
此方法内,获取所有的Provider对象。然后调用其authenticate()方法

3、AuthenticationProvider.authenticate() 用其子类DaoAuthenticationProvider来具体说明。在此方法内,
首先获取UserDetail
接着验证UserDetail
其次密码比对
**上述任何一步发生异常,则验证不通过。**到第5步

成功走到这一步,说明登陆验证通过。接着第4步
4、SuccessHandler.onAuthenticationSuccess()调用成功处理器

5、若其中发生AuthenticationException 类异常,调用
FailureHandler.onAuthenticationFailure().否则,只抛出异常,而不进入异常处理器

为了大概弄清楚这个验证流程,从没看过源码的我可废了不少功夫。首先是不知道那些类是干什么的。还有那么多的接口,调用的时候是用的哪一个接口的实现类。其次是断点大概需要在什么位置。每一个重要的方法中不认识的类或者方法是干什么的 等等都得一一去了解。。。 累 ~ ~,

你可能感兴趣的:(SpringSecurity(四)自定义失败与成功处理器)