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 ...
随机推荐
- 前台js发请求参数与后台接参数的问题
js函数中写参数,ajax中写参数,示例如下: function informationQuery(sign){//预警详情条件查询 $.get("/detail/informationQu ...
- 认识 vh 和 vw 单位
1. 页面 html 结构 <header> <h1>欢迎来到米修在线</h1> <p>Lorem ipsum dolor sit amet conse ...
- Hyperledger Fabric 通道配置文件和容器环境变量详解
摘要 Fabric 网络启动的过程中需要进行大量配置,新学时对各个配置的作用一无所知,这导致我曾在网络出问题时先对配置文件的内容进行排列组合后再祈祷它能在某个时刻顺利运行,因此掌握 fabric 各个 ...
- Android 12(S) 图像显示系统 - SurfaceFlinger之VSync-上篇(十六)
必读: Android 12(S) 图像显示系统 - 开篇 一.前言 为了提高Android系统的UI交互速度和操作的流畅度,在Android 4.1中,引入了Project Butter,即&quo ...
- XCTF练习题---WEB---backup
XCTF练习题---WEB---backup flag:Cyberpeace{855A1C4B3401294CB6604CCC98BDE334} 解题步骤: 1.观察题目,打开场景 2.打开以后发现是 ...
- C#关于在返回值为Task方法中使用Thread.Sleep引发的思考
起因 最近有个小伙伴提出了一个问题,就是在使用.net core的BackgroundService的时候,对应的ExecuteAsync方法里面写如下代码,会使程序一直卡在当前方法,不会继续执行,代 ...
- srpingboot拦截器
1.创建一个普通的web工程. 2.先创建需要的基础文件,比如一个用户类. package com.example.mode; public class User { private Integer ...
- python之函数的进阶
1.名称空间: 定义:用来存放名字的(变量,函数名,类名,引入的模块名) 分类: 内置名称空间:python解释器提供好的一些内置内容 全局名称空间:py文件中自己写的变量 局部名称空间:执行函数时会 ...
- awk-文本处理【中文手册版】
01. 简介 AWK是一个文本(面向行和列)处理工具,同时它也是一门脚本语言. AWK其名称得自于它的创始人 Alfred Aho .Peter Weinberger 和 Brian Kernigha ...
- 02-C高级编程
Day01 笔记 1 typedef使用 1.1 起别名 - 简化struct关键字 1.2 区分数据类型 1.3 提高代码移植性 2 void使用 2.1 不可以利用void创建变量 无法给无类型变 ...