​SpringSecurity-7-自定义AuthenticationProvider实现图形验证码

SpringSecurity-7-自定义AuthenticationProvider实现图形验证码

上一章节我们介绍了如何使用过滤器(Filter)实现图形验证,这是属于Servlet层面,比较简单容易理解。那么这次我们介绍SpringSecurity提供的另一种比较高端的实现图形化验证码,这就是AuthenticationProvider自定义认证。

认证流程

我们在

图片

其中介绍了系统的用户信息,保存在SpringSecurity的主体(Principal)中。主体中包含了所有经过验证用户的权限,详细信息等内容。在SpringSecurity中将其封装放在Authentication中,代码如下

    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;
    }

说明:

  • Authentication中包含主体权限列表,主体凭据,主体的详细信息,及是否验证成功等。

  • AuthenticationProvider被SpringSecurity定义为一个验证过程

  • ProviderManager管理多个AuthenticationProvider

UsernamePasswordAuthenticationFilter

我们查看UsernamePasswordAuthenticationFilter类发现设置用户信息的方法setDetails方法

图片

从源码我们可以看出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地址。源码如图所示

图片

自定义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();
    }
}

主要修改如图

图片

测试


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

图片

如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值