0
点赞
收藏
分享

微信扫一扫

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

玉字璧 2022-03-26 阅读 179

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

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

认证流程

  • ​​Filter实现图形化验证码​​

我们在

  • ​​SpringSecurity认证流程源码解析​​中介绍了SpringSecurity的认证流程

​SpringSecurity-7-自定义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方法

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

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

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

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

主要修改如图

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

测试

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

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


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

公众号 ​ springboot葵花宝典 ​ 主要分享JAVA技术,主要包含SpringBoot、SpingCloud、Docker、中间件等技术,以及Github开源项目

原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!

举报

相关推荐

0 条评论