SpringSecurity 进行自定义Token校验
背景
Spring Security默认使用「用户名/密码」的方式进行登陆校验,并通过cookie的方式存留登陆信息。在一些定制化场景,比如希望单独使用token串进行部分页面的访问权限控制时,默认方案无法支持。在未能在网上搜索出相关实践的情况下,通过官方文档及个别Stack Overflow的零散案例,形成整体思路并实践测试通过,本文即关于该方案的一个分享。
参考
官方文档:https://docs.spring.io/spring-security/site/docs/5.0.5.BUILD-SNAPSHOT/reference/htmlsingle/
SpringSecurity校验流程
基本的SpringSecurity使用方式网上很多,不是本文关注的重点,如有需要可以自行搜索或参见https://blog.csdn.net/u012702547/article/details/54319508
关于校验的整个流程简单的说,整个链路有三个关键点,
- 将需要鉴权的类/方法/url),定义为需要鉴权(本文代码示例为方法上注解@PreAuthorize("hasPermission('TARGET','PERMISSION')")
- 根据访问的信息产生一个来访者的权限信息Authentication,并插入到上下文中
- 在调用鉴权方法时,根据指定的鉴权方式,验证权限信息是否符合权限要求
完整的调用链建议在IDE中通过单步调试亲自体会,本文不做相关整理。
如何自定义
我的需求,是使用自定义的token,验证权限,涉及到:
- 产生Authentication并插入到上下文中
- 针对token的验证方式
需要做的事情如下:
- 自定义TokenAuthentication类,实现org.springframework.security.core.Authenticaion,作为token权限信息
- 自定义AuthenticationTokenFilter类,实现javax.servlet.Filter,在收到访问时,根据访问信息生成TokenAuthentication实例,并插入上下文
- 自定义SecurityPermissionEvalutor类,实现org.springframework.security.access.PermissionEvaluator,完成权限的自定义验证逻辑
- 在全局的配置中,定义使用SecurityPermissionEvalutor作为权限校验方式
TokenAuthentication.java
/**
* @author: Blaketairan
*/
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.ArrayList;
import java.util.Collection;
/**
* Description: spring-security的Authentication的自定义实现(用于校验token)
*/
public class TokenAuthentication implements Authentication{
private String token;
public TokenAuthentication(String token){
this.token = token;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return new ArrayList<GrantedAuthority>(0);
}
@Override
public Object getCredentials(){
return token;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return null;
}
}
AuthenticationTokenFilter.java
/**
* @author: Blaketairan
*/
import com.google.common.base.Strings;
import com.blaketairan.spring.security.configuration.TokenAuthentication;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Description: 用于处理收到的token并为spring-security上下文生成及注入Authenticaion实例
*/
@Configuration
public class AuthenticationTokenFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException{
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain)
throws IOException, ServletException{
if (servletRequest instanceof HttpServletRequest){
String token = ((HttpServletRequest) servletRequest).getHeader("PRIVATE-TOKEN");
if (!Strings.isNullOrEmpty(token)){
Authentication authentication = new TokenAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println("Set authentication with non-empty token");
} else {
/**
* 在未收到Token时,至少塞入空TokenAuthenticaion实例,避免进入SpringSecurity的用户名密码默认模式
*/
Authentication authentication = new TokenAuthentication("");
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println("Set authentication with empty token");
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy(){
}
}
SecurityPermissionEvalutor.java
/**
* @author: Blaketairan
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import java.io.Serializable;
/**
* Description: spring-security 自定义的权限处理模块(鉴权)
*/
public class SecurityPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication,Object targetDomainObject, Object permission){
String targetDomainObjectString = null;
String permissionString = null;
String token = null;
try {
targetDomainObjectString = (String)targetDomainObject;
permissionString = (String)permission;
token = (String)authentication.getCredentials();
} catch (ClassCastException e){
e.printStackTrace();
return false;
}
return hasPermission(token, targetDomainObjectString, permissionString);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission){
/**
* 使用@PreAuthorize("hasPermission('TARGET','PERMISSION')")方式,不使用该鉴权逻辑
*/
return false;
}
private boolean hasPermission(String token,String targetDomain, String permission){
/**
* 验证权限
**/
return true;
}
}
SecurityConfig.java 全局配置
/**
* @author: Blaketairan
*/
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Description: spring-security配置,指定使用自定义的权限评估方法
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception{
return super.authenticationManager();
}
@Bean
public PermissionEvaluator permissionEvaluator() {
/**
* 使用自定义的权限验证
**/
SecurityPermissionEvaluator securityPermissionEvaluator = new SecurityPermissionEvaluator();
return securityPermissionEvaluator;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
/**
* 关掉csrf方便本地ip调用调试
**/
httpSecurity
.csrf()
.disable()
.httpBasic()
.disable();
}
}
BaseRepository.java 某个需要权限验证的方法
/**
* @author: Blaketairan
*/
import org.springframework.security.access.prepost.PreAuthorize;
import java.util.List;
/**
* Description:
*/
public interface BaseRepository{
@PreAuthorize("hasPermission('DOMAIN', 'PERMISSION')")
void deleteAll();
}
结语
希望对看到本文的人有所帮助。
SpringSecurity 进行自定义Token校验的更多相关文章
- Spring Cloud Gateway 实现Token校验
在我看来,在某些场景下,网关就像是一个公共方法,把项目中的都要用到的一些功能提出来,抽象成一个服务.比如,我们可以在业务网关上做日志收集.Token校验等等,当然这么理解很狭隘,因为网关的能力远不止如 ...
- django-jwt token校验源码简析
一. jwt token校验源码简析 1.1 前言 之前使用jwt签发了token,里面的头部包含了加密的方式.是否有签名等,而载荷中包含用户名.用户主键.过期时间等信息,最后的签名还使用了摘要算法进 ...
- 待实践二:MVC3下的3种验证 (1)前台 jquery validate验证 (2)MVC实体验证 (3)EF生成的/自己手写的 自定义实体校验(伙伴类+元素据共享)
MVC3下的3种验证 (1):前台Jquery Validate脚本验证 引入脚本 <script src="../js/jquery.js" type="text ...
- spring cloud 服务A调用服务B自定义token消失,记录
后端:spring cloud 前端:vue 场景:前端ajax请求,包装自定义请求头token到后台做验证,首先调用A服务,A服务通过Feign调用B服务发现自定义token没有传到B服务去; 原因 ...
- Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)
Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性) 前言:由于前段时间忙于写接口,在接口中需要做很多的参数校验,本着简洁.高效的原则,便写了这个小工具供自己使用(内容 ...
- 自定义token,保存到客户端的cookie中,
自定义token #原理自定义token,放入cookie中,不用存数据库 #token定义方式 >>>>> "加密字符串"|登陆用户id|用户登陆时 ...
- Struts2 自定义输入校验 第五弹
Struts2的校验框架有两种:一种是validate方法,另一种是有效的xml文件. Action中自定义方法的输入校验,对于通过action的method属性所指定的自定义方法myExecute, ...
- Java 自定义注解 校验指定字段对应数据库内容重复
一.前言 在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等... 而像验证这类代码如下: 那么有没有办法可以解决这类似的重复代码量呢? 我们可以通过自定义注解校验的方式去 ...
- 更加灵活的参数校验,Spring-boot自定义参数校验注解
上文我们讨论了如何使用@Min.@Max等注解进行参数校验,主要是针对基本数据类型和级联对象进行参数校验的演示,但是在实际中我们往往需要更为复杂的校验规则,比如注册用户的密码和确认密码进行校验,这个时 ...
随机推荐
- POJ-1122 FDNY to the Rescue!---Dijkstra+反向建图
题目链接: https://vjudge.net/problem/POJ-1122 题目大意: 给出矩阵,矩阵中每个元素tij表示从第i个交叉路口到第j个交叉路口所需时间,若tij为-1则表示两交叉路 ...
- [翻译] softmax和softmax_cross_entropy_with_logits的区别
翻译自:https://stackoverflow.com/questions/34240703/whats-the-difference-between-softmax-and-softmax-cr ...
- 04、NetCore2.0下Web应用之Startup源码解析
04.NetCore2.0Web应用之Startup源码解析 通过分析Asp.Net Core 2.0的Startup部分源码,来理解插件框架的运行机制,以及掌握Startup注册的最优姿势. - ...
- Azure AI 服务之文本翻译
当下人工智能可谓是风头正劲,几乎所有的大厂都有相关的技术栈.微软在 AI 领域自然也是投入了重注,并且以 Azure 认知服务的方式投入了市场: 也就是说作为开发者我们不需要学习太多 AI 的理论知识 ...
- 关于阮大神的es6标准入门第一章
题记:之前在10月份的时候写过阮大神的es6的第一章,但是由于那段时间项目组的动荡,所以也没有什么后续,导致我现在对es6基本都忘的差不多了,不过,现在换了新公司,最近也没什么任务,所以现在开始重新写 ...
- ES6 new syntax of let and const (one)
variable declarations : let, const,and block scope why we redefine the way about declarations? funct ...
- [LeetCode] My Calendar I 我的日历之一
Implement a MyCalendar class to store your events. A new event can be added if adding the event will ...
- [LeetCode] Minimum Index Sum of Two Lists 两个表单的最小坐标和
Suppose Andy and Doris want to choose a restaurant for dinner, and they both have a list of favorite ...
- [SDOI2011]黑白棋
Description 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同. 小 ...
- ●BZOJ 2005 NOI 2010 能量采集
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2005 题解: 一个带有容斥思想的递推.%%% 首先,对于一个点 (x,y) 在路径 (0,0 ...