Spring Security自定义认证器
在了解过Security的认证器后,如果想自定义登陆,只要实现AuthenticationProvider还有对应的Authentication就可以了
Authentication
首先要创建一个自定义的Authentication,Security提供了一个Authentication的子类AbstractAuthenticationToken
我们实现这个类可以了,他已经实现了Authentication的一些方法
public class NamePassAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 520L;
private final Object principal;
private Object credentials;
//提供第一次进来的构造方法
public NamePassAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
//提供填充Authentication的构造方法
public NamePassAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
这个类关键就是一个是认证的,一个没认证的的构造器
AuthenticationProvider
接着是AuthenticationProvider,需要实现他的authenticate方法
@Setter
public class NamePassAuthenticationProvider implements AuthenticationProvider {
private CustomUserDetailsService userDetailsService;
private PasswordEncoder passwordEncoder;
@Override
//具体认证逻辑
public Authentication authenticate(Authentication authentication) {
NamePassAuthenticationToken authenticationToken = (NamePassAuthenticationToken) authentication;
String username = (String) authenticationToken.getPrincipal();
String password = (String) authenticationToken.getCredentials();
//让具体认证类去认证
UserDetails user = userDetailsService.loadUserByUsername(username);
boolean matches = passwordEncoder.matches(password, user.getPassword());
if (!matches) {
ResMsg.throwException(AuthExceptionGroup.AUTH_ERROR);
}
//填充Authentication
NamePassAuthenticationToken authenticationResult = new NamePassAuthenticationToken(user, password, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
//指定具体的Authentication
//根据你指定的Authentication来找到具体的Provider
public boolean supports(Class<?> authentication) {
return NamePassAuthenticationToken.class.isAssignableFrom(authentication);
}
}
SecurityConfigurerAdapter
接着就是填充配置了
@Component
public class NamePassAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(HttpSecurity http) {
//phonePass provider
NamePassAuthenticationProvider provider = new NamePassAuthenticationProvider();
provider.setUserDetailsService(customUserDetailsService);
provider.setPasswordEncoder(passwordEncoder);
http.authenticationProvider(provider);
}
}
接下来就是导入配置了
通常都会有一个实现了WebSecurityConfigurerAdapter的配置类
把配置类注入进来
@Autowired
private NamePassAuthenticationSecurityConfig namePassAuthenticationSecurityConfig;
protected void configure(HttpSecurity http) throws Exception {
http.apply(namePassAuthenticationSecurityConfig);
}
UserDetailsService
UserDetailsService是具体的认证实现类
这个类就非常熟悉了,只需要实现他的loadUserByUsername方法,就可以实现认证了
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AuthmsViewAccount account = accountService.getAccount(username);
if(account == null) {
ResMsg.throwException(AUTH_ERROR);
}
if (account.getStatus() != 1) {
ResMsg.throwException(ACCOUNT_HAS_BANED);
}
String spliceStaffInfo = String.format("%d-%s",account.getAccountId(),account.getUsername());
//只要Collection<? extends GrantedAuthority> authorities
//这个参数不为空,就表明认证通过,所以空集合也可以通过
return new User(spliceStaffInfo,account.getPassword(), AuthorityUtils.NO_AUTHORITIES);
}
把认证结果填充到上下文中
TokenFilter
如果结合了Token,那么需要从token中识别该用户
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String bearerToken = resolveToken(request);
if (bearerToken != null && !"".equals(bearerToken.trim()) && SecurityContextHolder.getContext().getAuthentication() == null) {
//从redis中获取该用户
NamePassAuthenticationToken namePassAuthenticationToken = authRedisHelper.get(bearerToken);
if(namePassAuthenticationToken != null) {
//将信息保存到上下文中
SecurityContextHolder.getContext().setAuthentication(namePassAuthenticationToken);
}
}
chain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
return bearerToken.substring(7);
}
return null;
}
public NamePassAuthenticationToken get(String bearerToken){
String spliceStaffInfo = (String)redisRepository.get(formatKey(bearerToken));
if(spliceStaffInfo == null) {
return null;
}
return new NamePassAuthenticationToken(new AuthStaff(spliceStaffInfo),null,AuthorityUtils.NO_AUTHORITIES);
}
登录过程
在登录的时候,就需要用到这个自定义的认证器了
// 通过用户名和密码创建一个 Authentication 认证对象,实现类为 NamePassAuthenticationToken
NamePassAuthenticationToken authenticationToken = new NamePassAuthenticationToken(user.getUsername(), user.getPassword());
//通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证 Authentication 对象
//AuthenticationManager会通过你传入的authenticationToken来找到具体的Provider
Authentication authentication = authenticationManager.authenticate(authenticationToken);
//填充用户信息到secrity中的user里
User principal = (User) authentication.getPrincipal();
//获取认证后的信息
NamePassAuthenticationToken namePassAuthenticationToken = new NamePassAuthenticationToken(new AuthStaff(principal.getUsername()), null, authentication.getAuthorities());
// 生成token
String bearerToken = IdUtil.fastSimpleUUID();
// 加载到reids
authRedisHelper.set(bearerToken, namePassAuthenticationToken);
这样就实现了自定义的认证器了
Spring Security自定义认证器的更多相关文章
- Spring Security自定义认证页面(动态网页解决方案+静态网页解决方案)--练气中期圆满
写在前面 上一回我们简单分析了spring security拦截器链的加载流程,我们还有一些简单的问题没有解决.如何自定义登录页面?如何通过数据库获取用户权限信息? 今天主要解决如何配置自定义认证页面 ...
- spring security自定义指南
序 本文主要研究一下几种自定义spring security的方式 主要方式 自定义UserDetailsService 自定义passwordEncoder 自定义filter 自定义Authent ...
- Spring Security 接口认证鉴权入门实践指南
目录 前言 SpringBoot 示例 SpringBoot pom.xml SpringBoot application.yml SpringBoot IndexController SpringB ...
- Spring Security 自定义登录认证(二)
一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Se ...
- Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理
本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...
- (二)spring Security 自定义登录页面与校验用户
文章目录 配置 security 配置下 MVC 自定义登录页面 自定义一个登陆成功欢迎页面 效果图 小结: 使用 Spring Boot 的快速创建项目功能,勾选上本篇博客需要的功能:web,sec ...
- 解决Spring Security自定义filter重复执行问题
今天做项目的时候,发现每次拦截器日志都会打两遍,很纳闷,怀疑是Filter被执行了两遍.结果debug之后发现还真是!记录一下这个神奇的BUG! 问题描述 项目中使用的是Spring-security ...
- 最简单易懂的Spring Security 身份认证流程讲解
最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...
- 02 spring security 自定义用户认证流程
1. 自定义登录页面 (1)首先在static目录下面创建login.html 注意: springboot项目默认可以访问resources/resources, resources/s ...
随机推荐
- docker基础容器中bash: vi: command not found问题解决
安装即可 1.apt-get update 2.apt-get install vim
- mysql查询 if判断、case语句的使用等
一. 查询的数字转换为中文返回前端 1. 如果是0或1表状态等,可用: IF(字段 = 0, '否', '是') AS xxx 2. 如果是多个值,比如1,2,3可用: ELT(字段, '计划治理', ...
- 十、包机制与JavaDoc
一.包机制 为了更好的组织类,Java提供了包机制,用于区别类名的命名空间. 包语句的语句格式为: package pkg1[. pkg2[. pkg3...]]; 一般使用公司域名倒置作为包名:例如 ...
- flink调优之RocksDB设置
一.开启监控 RocksDB是基于LSM Tree实现的,写数据都是先缓存到内存中,所以RocksDB的写请求效率比较高.RocksDB使用内存结合磁盘的方式来存储数据,每次获取数据时,先从内存中bl ...
- SpringMVC踩坑2
Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exc ...
- Java第十四周作业
2.设计2个类,要求如下:(知识点:类的继承 方法的覆 盖) [必做题] 2.1 定义一个汽车类Vehicle, 2.1.1 属性包括:汽车品牌brand(String类型).颜色clr (Strin ...
- Ubuntu 20.0.4 安装 NVIDIA N卡 驱动 画面撕裂 解决方法
电脑 联想 Y7000 系统 Ubuntu 20.0.4 显卡 NVIDIA 1050TI 以下操作需要管理员权限 编辑文件,如果没有新建一个 /lib/modprobe.d/nvidia-graph ...
- Python实现双X轴双Y轴绘图
诈尸人口回归.这一年忙着灌水忙到头都掉了,最近在女朋友的提醒下终于想起来博客的账号密码,正好今天灌水的时候需要画一个双X轴双Y轴的图,研究了两小时终于用Py实现了.找资料的过程中没有发现有系统的文章, ...
- 树莓派开发笔记(十二):入手研华ADVANTECH工控树莓派UNO-220套件(一):介绍和运行系统
前言 树莓派也可以做商业应用,工业控制,其稳定性和可靠性已经得到了验证,故而工业控制,一些停车场等场景也有采用树莓派作为主控的,本片介绍了研华ADVANTECH的树莓派套件组UNO-220-P4N ...
- AcWing-3167. 星星还是树 -c++题解(模拟退火)
在二维平面上有 n 个点,第 i 个点的坐标为 (xi,yi).请你找出一个点,使得该点到这 n个点的距离之和最小.该点可以选择在平面中的任意位置,甚至与这 n个点的位置重合. 输入格式 第一行包 ...