Spring securty<九> 鉴权-自定义鉴权规则
文章目录
本地项目的基础环境
环境 | 版本 |
---|---|
jdk | 1.8.0_201 |
maven | 3.6.0 |
Spring-boot | 2.3.3.RELEASE |
1、简介
spring security是一个提供身份验证、授权和防止常见攻击的框架,它对命令式和反应式应用程序都有一流的支持,是保护基于Spring的应用程序的事实上的标准。详细可以参看《spring security官网》
《Spring securty<五> 认证–帐号/邮箱/手机号+密码》
《Spring securty<六> 认证–手机号+验证码》
《Spring securty<七> 认证–匿名用户拦截器源码分析》
《Spring securty<八> 鉴权–框架默认鉴权案例代码》
2、特性
2.1、身份验证
springsecurity作为身份验证,身份验证是验证试图访问特定资源的用户的身份的方法。对用户进行身份验证的常见方法是要求用户输入用户名和密码。一旦执行身份验证,我们就知道身份并可以执行授权。
2.2、资源保护,防止跨站点请求伪造(CSRF)
springsecurity提供了针对常见漏洞利用的保护。只要可能,默认情况下都会启用保护。
3、自定义鉴权规则
在上一篇的编码过程中,可以看到,每次权限的过程中,都需要去配置,或者项目启动的时候,去查询数据库,然后预定义进到代码里;
antMatchers("/test").hasRole("test").anyRequest().hasAuthority("admin");
或者通过注解的形式,编码到具体的请求上,这样,每次新增权限,或者修改权限的时候,就要修改代码,或者重启项目预定义会在项目启动的时候,加载到代码里);这样的缺点也是非常明显的;
并且每次异常的时候,都是返回的系统的英文异常,需要返回中文的指定异常;
有没有,通过权限的页面,对角色或者用户授权后,不需要重启项目或者修改的代码的方法?当然有,这个就需要自定义鉴权规则了;
下面就开始代码的编写了
3.1、复制项目代码
复制《Spring securty<八> 鉴权–框架默认鉴权案例代码》中的案例代码badger-spring-security-7
;
3.2、创建鉴权的接口
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
/**
* 接口鉴权的方法
* @author liqi
*/
public interface ResourcePermissionProcessor {
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
3.3、鉴权接口的实现
package com.badger.spring.boot.security.config.access;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
/**
* 接口鉴权的方法
* @author liqi
*/
@Component("resourcePermissionService")
public class ResourcePermissionService implements ResourcePermissionProcessor {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
public static final Map<String, List<String>> AUTH_MAP = new HashMap<>();
static {
AUTH_MAP.put("admin", Arrays.asList("/admin"));
AUTH_MAP.put("ROLE_test", Arrays.asList("/test"));
}
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
// 当前用户所具有的权限
final Set<String> authorityListToSet = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
final String url = request.getRequestURI();// 当前请求的url
for (String roleName : authorityListToSet) {
List<String> list = AUTH_MAP.get(roleName);
if (list != null) {
final Optional<String> findAny = list.stream().filter(res -> {
// 通配符路径验证 eg: /sys/login/** --表示任意路径 "*"号
return antPathMatcher.match(res, url);
}).findAny();
if (findAny.isPresent()) {
return true;
}
}
}
// 异常不是鉴权异常的时候,异常无法向上抛出,异常处理的controller无法返回默认异常,需要把异常处理成鉴权异常
throw new AuthenticationServiceException("没有访问url的权限:" + url);
}
}
代码实现就比较简单了,从上下文中,拿到请求HttpServletRequest request
以及,当前登录用户的会话信息Authentication authentication
;
然后拿到请求的url以及当前用户的角色信息,去跟数据库匹配就可以了,我这里,只是模拟了数据库的查询操作;
并且如果,鉴权失败了,还可以返回指定的中文异常;当然,你要是返回false
,也是会返回系统的英文异常;
3.4、配置WebSecurityConfig类
之前的配置
http.authorizeRequests().antMatchers(EXCLUDE_URLS).permitAll().antMatchers("/test").hasRole("test")
.anyRequest().hasAuthority("admin");
替换的配置
http.authorizeRequests().antMatchers(EXCLUDE_URLS).permitAll().anyRequest()
.access("@resourcePermissionService.hasPermission(request,authentication)");
@
:符号,固定写法
resourcePermissionService
:为注入的实例的名称;
hasPermission
:为方法名称
(request,authentication)
:从容器中,拿到的参数对象
4、测试演示
就不演示了,参考上一篇《Spring securty<八> 鉴权–框架默认鉴权案例代码》
详细的代码,可以查看《码云》