SpringSecurity自定义AuthenticationProvider和AuthenticationFilter
AuthenticationProvider
- 默认实现:DaoAuthenticationProvider
授权方式提供者,判断授权有效性,用户有效性,在判断用户是否有效性,它依赖于UserDetailsService实例,开发人员可以自定义UserDetailsService的实现。
- additionalAuthenticationChecks方法校验密码有效性
- retrieveUser方法根据用户名获取用户
- createSuccessAuthentication完成授权持久化
@Component
@Slf4j
public class LindAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Autowired
UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 校验密码有效性.
*
* @param userDetails .
* @param authentication .
* @throws AuthenticationException .
*/
@Override
protected void additionalAuthenticationChecks(
UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
/**
* 获取用户.
*
* @param username .
* @param authentication .
* @return
* @throws AuthenticationException .
*/
@Override
protected UserDetails retrieveUser(
String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser = userDetailsService.loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
}
/**
* 授权持久化.
*/
@Override
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
return super.createSuccessAuthentication(principal, authentication, user);
}
AuthenticationFilter
- 默认实现:UsernamePasswordAuthenticationFilter
授权过滤器,你可以自定义它,并把它添加到默认过滤器前或者后去执行,主要用来到授权的持久化,它可以从请求上下文中获取你的user,password等信息,然后去判断它是否符合规则,最后通过authenticate方法去授权。默认的UsernamePasswordAuthenticationFilter
过滤器,主要判断请求方式是否为post,并且对username和password进行了默认值的处理,总之,在这个过滤器里不会涉及到具体业务。
public class LindUserNameAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public LindUserNameAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "GET"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (username == null) {
throw new InternalAuthenticationServiceException("Failed to get the username");
}
if (password == null) {
throw new InternalAuthenticationServiceException("Failed to get the password");
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
}
UserDetialsService
这是一个接口,有默认的实现方式,一般的,我们需要根据业务去重新实现它,比如从你的用户表获取当前授权的用户信息,你需要在UserDetialsService实现类里对用户表进行读取操作;它一般会在AuthenticationProvider里的retrieveUser方法中被使用,这就像面向对象里的模板方法模式一样,springSecurity把检验的步骤设计好了,咱们开发只要根据规则去实现具体细节就好。
@Component
public class MyUserDetailService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
/*
设置用户和角色需要注意:
1. commaSeparatedStringToAuthorityList放入角色时需要加前缀ROLE_,而在controller使用时不需要加ROLE_前缀
2. 放入的是权限时,不能加ROLE_前缀,hasAuthority与放入的权限名称对应即可
*/
List<UserDetails> userDetailsList = new ArrayList<>();
userDetailsList.add(User.builder()
.username("admin")
.password(passwordEncoder.encode("123"))
.authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("read,ROLE_ADMIN")).build());
userDetailsList.add(User.builder()
.username("user")
.password(passwordEncoder.encode("123"))
.authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("read,ROLE_USER"))
.build());
//获取用户
return userDetailsList.stream()
.filter(o -> o.getUsername().equals(name))
.findFirst()
.orElse(null);
}
}
在WebSecurityConfig里开启它
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
LindAuthenticationSuccessHandler lindAuthenticationSuccessHandler;
@Autowired
LindAuthenticationFailHandler lindAuthenticationFailHandler;
@Autowired
LindAuthenticationProvider lindAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/index").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")//按路由授权
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/hello")//默认登录成功后跳转的页面
.successHandler(lindAuthenticationSuccessHandler)
.failureHandler(lindAuthenticationFailHandler)
.permitAll()
.and()
.addFilterAt(lindAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).authorizeRequests().and()
.logout()
.permitAll();
}
/**
* 自定义的Filter.
*/
@Bean
LindUserNameAuthenticationFilter lindAuthenticationFilter() {
LindUserNameAuthenticationFilter phoneAuthenticationFilter = new LindUserNameAuthenticationFilter();
ProviderManager providerManager =
new ProviderManager(Collections.singletonList(lindAuthenticationProvider));
phoneAuthenticationFilter.setAuthenticationManager(providerManager);
phoneAuthenticationFilter.setAuthenticationSuccessHandler(lindAuthenticationSuccessHandler);
phoneAuthenticationFilter.setAuthenticationFailureHandler(lindAuthenticationFailHandler);
return phoneAuthenticationFilter;
}
/**
* 密码生成策略.
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
认证时执行的顺序
- LindUserNameAuthenticationFilter
- LindAuthenticationProvider.retrieveUser
- LindAuthenticationProvider.additionalAuthenticationChecks
- UserDetailsService
- Authentication
springSecurity源码:https://github.com/spring-projects/spring-security
SpringSecurity自定义AuthenticationProvider和AuthenticationFilter的更多相关文章
- SpringSecurity 自定义用户 角色 资源权限控制
SpringSecurity 自定义用户 角色 资源权限控制 package com.joyen.learning.security; import java.sql.ResultSet; impor ...
- SpringSecurity 自定义表单登录
SpringSecurity 自定义表单登录 本篇主要讲解 在SpringSecurity中 如何 自定义表单登录 , SpringSecurity默认提供了一个表单登录,但是实际项目里肯定无法使用的 ...
- SpringSecurity自定义登陆页面和跳转页面
如果我们不用form-login说明登陆界面,springsecurity框架将自动为我们生成登陆界面 现在我们不想用自动生成的登陆界面了,而想使用自定义的漂亮的登陆界面 则需要使用<secur ...
- springSecurity自定义认证配置
上一篇讲了springSecurity的简单入门的小demo,认证用户是在xml中写死的.今天来说一下自定义认证,读取数据库来实现认证.当然,也是非常简单的,因为仅仅是读取数据库,权限是写死的,因为相 ...
- SpringSecurity自定义过滤器
applicationContext-security.xml: <beans:beans xmlns="http://www.springframework.org/schema/s ...
- Spring-Security 自定义Filter完成验证码校验
Spring-Security的功能主要是由一堆Filter构成过滤器链来实现,每个Filter都会完成自己的一部分工作.我今天要做的是对UsernamePasswordAuthenticationF ...
- SpringSecurity自定义UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter介绍 UsernamePasswordAuthenticationFilter是AbstractAuthenticationPr ...
- SpringSecurity自定义注解和处理器
登录功能 添加一个配置类 @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Reso ...
- Spring-Security自定义登录页&inMemoryAuthentication验证
Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架.框架下内容比较多,可以做到按照角色权限对请求路径进行限制.今天主要验证自定义登录页,在内存用户存储中进行请求 ...
随机推荐
- engine.go
package ;;;;;;;;) ;;;;;) ) ) ) ;; ;;, types.DocumentIndexData{}, true) for { runtime.Gos ...
- BZOJ_4128_Matrix_矩阵乘法+哈希+BSGS
BZOJ_4128_Matrix_矩阵乘法+哈希+BSGS Description 给定矩阵A,B和模数p,求最小的x满足 A^x = B (mod p) Input 第一行两个整数n和p,表示矩阵的 ...
- HttpClient4 TIME_WAIT和CLOSE_WAIT
最近,公司的接口服务器(客户端,向外发送数据)频繁出现了connect timeout 以及readtime out 的情况,经过运维平台检测,并没有网络延时的情况.于是,开始怀疑连接池出了问题. 使 ...
- 如何删除git远程仓库项目的所有内容,重新提交所有内容
如果我们上传了一个项目到git并已经commit和push了所有内容,但是忘记搞gitignore文件, 导致一些不想加入版本控制的文件,如IDE配置文件,编译文件,部署文件等, 现在不知道怎么办了? ...
- 浅谈.Net异步编程的前世今生----APM篇
前言 在.Net程序开发过程中,我们经常会遇到如下场景: 编写WinForm程序客户端,需要查询数据库获取数据,于是我们根据需求写好了代码后,点击查询,发现界面卡死,无法响应.经过调试,发现查询数据库 ...
- python基于selenium实现自动删除qq空间留言板
py大法好,让你解放双手. 脚本环境 python环境,selenium库,Chrome webdriver驱动等. 源码 # coding=utf-8 import datetime import ...
- python中的shutil模块
目录 python中的shutil模块 目录和文件操作 归档操作 python中的shutil模块 shutil模块对文件和文件集合提供了许多高级操作,特别是提供了支持文件复制和删除的函数. 目录和文 ...
- 通过改进团队流程最大限度发挥Scrum的优势
团队如何最大限度地发挥Scrum和敏捷的优势? 回想一下,Scrum团队在Scrum的框架内定义了自己的流程.这其中包括方法.工具和互动以及如何履行Scrum角色的职责.如何使用工件和事件等. 如何确 ...
- 服务端预渲染之Nuxt (使用篇)
服务端预渲染之Nuxt - 使用 现在大多数开发都是基于Vue或者React开发的,能够达到快速开发的效果,也有一些不足的地方,Nuxt能够在服务端做出渲染,然后让搜索引擎在爬取数据的时候能够读到当前 ...
- Vue.js 学习笔记 第2章 数据绑定和第一个Vue应用
本篇目录: 2.1 Vue实例与数据绑定 2.2 指令与事件 2.3 语法糖 学习任何一种框架,从一个Hello World应用开始是最快了解该框架特性的途径. 我们先从一段简单的HTML代码开始,感 ...