SpringSecurity-7-Custom AuthenticationProvider implements graphic verification code

SpringSecurity-7-Custom AuthenticationProvider implements graphic verification code

In the previous chapter, we introduced how to use the filter (Filter) to implement graphic verification, which belongs to the Servlet level and is relatively simple and easy to understand. Then this time we introduce another high-end implementation of graphical verification code provided by Spring Security, which is AuthenticationProvider custom authentication.

Certification process

we are at

picture

It introduces the user information of the system, which is stored in the Principal of Spring Security. The body contains the permissions, details, etc. of all authenticated users. In Spring Security, it is encapsulated in Authentication, the code is as follows

    public interface Authentication extends Principal, Serializable {
        /**
         * 获取用户权限
         * @return
         */
        Collection<? extends GrantedAuthority> getAuthorities();
        /**
         * 获取用于的凭证,用户密码
         * @return
         */
        Object getCredentials();
        /**
         * 用户的详细信息
         * @return
         */
        Object getDetails();
        /**
         * 用户凭证,一般为用户名
         * @return
         */
        Object getPrincipal();
        /**
         * 用户验证是否成功
         * @return
         */
        boolean isAuthenticated();
        void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
    }

复制代码

illustrate:

  • Authentication contains a list of subject permissions, subject credentials, subject details, and whether the authentication is successful.

  • AuthenticationProvider is defined by SpringSecurity as an authentication process

  • ProviderManager manages multiple AuthenticationProviders

UsernamePasswordAuthenticationFilter

We look at the UsernamePasswordAuthenticationFilter class and find the method setDetails method for setting user information

picture

从源码我们可以看出authenticationDetailsSource是由AbstractAuthenticationProcessingFilter提供的AbstractAuthenticationProcessingFilter部分源码如下

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
      implements ApplicationEventPublisherAware, MessageSourceAware {

   protected ApplicationEventPublisher eventPublisher;

   protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
     ...
   }

复制代码

WebAuthenticationDetailsSource

在UsernamePasswordAuthenticationFilter中使用的AuthenticationDetailsSource是一个标准的Web认证 源,携带的是用户的sessionId和IP地址。源码如图所示

picture

自定义WebAuthenticationDetails

有了HttpServletRequest之后,一切都将变得非常顺畅。基于图形验证码的场景,我们可以继承 WebAuthenticationDetails,并扩展需要的信息。因此我们可以自定义WebAuthenticationDetails存储额外信息。

/**
 *自定义WebAuthenticationDetails存储额外的图形验证信息
 */
public class ImageCodeWebAuthenticationDetails extends WebAuthenticationDetails {
    /**
     * 图形信息是否验证成功
     */
    private boolean imageCodeIsRight;

    public boolean getImageCodeIsRight(){
        return imageCodeIsRight;
    }
    public ImageCodeWebAuthenticationDetails(HttpServletRequest request) {
        super(request);
        // 先获取seesion中的验证码
        HttpSession session = request.getSession();
        String sessionCode = (String) session.getAttribute(CaptchaController.SESSION_KEY);
        // 获取用户输入的验证码
        String inpuCode = request.getParameter("code");
        if(!StringUtils.isEmpty(inpuCode)){
            //清除验证码,不论验证成功还是失败,都需要清除验证码,并且在验证失败的时候需要刷新验证码
            session.removeAttribute("code");
            if(!StringUtils.isEmpty(sessionCode)&& inpuCode.equalsIgnoreCase(sessionCode) ){
                this.imageCodeIsRight=true;
            }
        }

    }
}

复制代码

自定义的AuthenticationDetailsSource。

@Component("imageCodeWebAuthenticationDetailsSource")
public class ImageCodeWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
    @Override
    public ImageCodeWebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new ImageCodeWebAuthenticationDetails(context);
    }
}

复制代码

自定义AuthenticationProvider。

@Component("imageCodeAuthenticationProvider")
public class ImageCodeAuthenticationProvider extends DaoAuthenticationProvider {

    public ImageCodeAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.setUserDetailsService(userDetailsService);
        this.setPasswordEncoder(passwordEncoder);

    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        //获取详细信息
        ImageCodeWebAuthenticationDetails  details = (ImageCodeWebAuthenticationDetails)authentication.getDetails();
        //如果验证码不正确,抛出异常
        if(!details.getImageCodeIsRight()){
            throw new ValidateCodeException("验证码输入错误");
        }
        super.additionalAuthenticationChecks(userDetails, authentication);
    }

}

复制代码

修改配置类

想要应用自定义的 AuthenticationProvider 和 AuthenticationDetailsSource,还需在LearnSrpingSecurity中完成剩余的配置。

/**
 * 安全配置类
 */
@EnableWebSecurity
public class LearnSrpingSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("imageCodeWebAuthenticationDetailsSource")
    private AuthenticationDetailsSource<HttpServletRequest,WebAuthenticationDetails> imageCodeWebAuthenticationDetailsSource;

    @Autowired
    @Qualifier("imageCodeAuthenticationProvider")
    private AuthenticationProvider imageCodeAuthenticationProvider;
    /**
     * 认证管理器
     * 1.认证信息提供方式(用户名、密码、当前用户的资源权限)
     * 2.可采用内存存储方式,也可能采用数据库方式等
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);
        auth.authenticationProvider(imageCodeAuthenticationProvider);
    }
    /**
     * 资源权限配置(过滤器链):
     * 1、被拦截的资源
     * 2、资源所对应的角色权限
     * 3、定义认证方式:httpBasic 、httpForm
     * 4、定制登录页面、登录请求地址、错误处理方式
     * 5、自定义 spring security 过滤器
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable() //禁用跨站csrf攻击防御,后面的章节会专门讲解
                .formLogin()
                .authenticationDetailsSource(imageCodeWebAuthenticationDetailsSource)
                .loginPage("/login/page")//一旦用户的请求没有权限就跳转到这个页面
                .loginProcessingUrl("/login/form")//登录表单form中action的地址,也就是处理认证请求的路径
                .usernameParameter("username")///登录表单form中用户名输入框input的name名,不修改的话默认是username
                .passwordParameter("password")//form中密码输入框input的name名,不修改的话默认是password
                //.defaultSuccessUrl("/syslog")//登录认证成功后默认转跳的路径
                //.failureHandler(failureHandler)
                .and()
                .authorizeRequests()
                .antMatchers("/login/page","/code/image").permitAll()//不需要通过登录验证就可以被访问的资源路径
                .anyRequest().authenticated();
    }
}

复制代码

主要修改如图

picture

测试


我们使用浏览器浏览http://localhost:8888,输入错误的验证码,结果为

picture

如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!

springboot葵花宝典

主要分享JAVA技术,主要包含SpringBoot、SpingCloud、Docker、中间件等技术,以及Github开源项目

70篇原创内容

公众号

Originality is not easy, please indicate the source for reprinting, thank you for your support! If this article is useful to you, please share it!

This article uses the article synchronization assistant to synchronize

Guess you like

Origin juejin.im/post/7079383584640860174