背景

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校验的更多相关文章

  1. Spring Cloud Gateway 实现Token校验

    在我看来,在某些场景下,网关就像是一个公共方法,把项目中的都要用到的一些功能提出来,抽象成一个服务.比如,我们可以在业务网关上做日志收集.Token校验等等,当然这么理解很狭隘,因为网关的能力远不止如 ...

  2. django-jwt token校验源码简析

    一. jwt token校验源码简析 1.1 前言 之前使用jwt签发了token,里面的头部包含了加密的方式.是否有签名等,而载荷中包含用户名.用户主键.过期时间等信息,最后的签名还使用了摘要算法进 ...

  3. 待实践二:MVC3下的3种验证 (1)前台 jquery validate验证 (2)MVC实体验证 (3)EF生成的/自己手写的 自定义实体校验(伙伴类+元素据共享)

    MVC3下的3种验证 (1):前台Jquery Validate脚本验证 引入脚本 <script src="../js/jquery.js" type="text ...

  4. spring cloud 服务A调用服务B自定义token消失,记录

    后端:spring cloud 前端:vue 场景:前端ajax请求,包装自定义请求头token到后台做验证,首先调用A服务,A服务通过Feign调用B服务发现自定义token没有传到B服务去; 原因 ...

  5. Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)

    Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性) 前言:由于前段时间忙于写接口,在接口中需要做很多的参数校验,本着简洁.高效的原则,便写了这个小工具供自己使用(内容 ...

  6. 自定义token,保存到客户端的cookie中,

    自定义token #原理自定义token,放入cookie中,不用存数据库 #token定义方式 >>>>> "加密字符串"|登陆用户id|用户登陆时 ...

  7. Struts2 自定义输入校验 第五弹

    Struts2的校验框架有两种:一种是validate方法,另一种是有效的xml文件. Action中自定义方法的输入校验,对于通过action的method属性所指定的自定义方法myExecute, ...

  8. Java 自定义注解 校验指定字段对应数据库内容重复

    一.前言 在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等... 而像验证这类代码如下: 那么有没有办法可以解决这类似的重复代码量呢? 我们可以通过自定义注解校验的方式去 ...

  9. 更加灵活的参数校验,Spring-boot自定义参数校验注解

    上文我们讨论了如何使用@Min.@Max等注解进行参数校验,主要是针对基本数据类型和级联对象进行参数校验的演示,但是在实际中我们往往需要更为复杂的校验规则,比如注册用户的密码和确认密码进行校验,这个时 ...

随机推荐

  1. JavaScript中Global、Math、Date对象的常用方法

    JavaScript当中Global.Math.Date类型常用方法如下: /* js 中 Global对象 是一个不存在的对象,它里面的方法可以调用 常用方法: 1 encodeURI 对uri进行 ...

  2. Android WebView那些坑之上传文件

    最近公司项目需要在WebView上调用手机系统相册来上传图片,开发过程中发现在很多机器上无法正常唤起系统相册来选择图片. 解决问题之前我们先来说说WebView上传文件的逻辑:当我们在Web页面上点击 ...

  3. Java并发编程:synchronized和锁优化

    1. 使用方法 synchronized 是 java 中最常用的保证线程安全的方式,synchronized 的作用主要有三方面: 确保线程互斥的访问代码块,同一时刻只有一个方法可以进入到临界区 保 ...

  4. ubuntu16.04下安装chrome

    1.在终端中,输入以下命令: sudo wget http://www.linuxidc.com/files/repo/google-chrome.list -P /etc/apt/sources.l ...

  5. Java-Maven(七):Eclipse中Maven依赖、聚合、继承特性

    之前通过学习了解,maven集成到eclipse中的如何创建项目,以及maven命令插件在eclipse中安装后的用法.那么接下来我们将会学习一些maven在项目中的一些特性,及如何使用. Maven ...

  6. hdu1789 Doing Homework again---(经典贪心)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1789 题目大意: 给出N个作业的截至日期,和N个作业不交所扣掉的分数,要求输出扣除分数做少的方案. ...

  7. transition和animation做动画(css动画二)

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! translate:平移:是transform的一个属性: transform:变形:是一个静态属性,可以 ...

  8. DbContext(String)+SqlQuery一起使用

    DbContext(String) 可以将给定字符串用作将连接到的数据库的名称或连接字符串来构造一个新的上下文实例. Database.SqlQuery 方法 (Type, String, Objec ...

  9. ubuntu安装eclipse

    官网下载界面 这里我选择的是Exlipse Oxygen的Eclipse IDE for Java EE Developers的64位版本. IBM直接下载地址 下载下来的是一个tar.gz的安装包, ...

  10. 浅析java的深拷贝与浅拷贝

    (转自:http://www.cnblogs.com/chenssy/p/3308489.html) 首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式 ...