ADD SSM鉴权
This commit is contained in:
parent
0688283cbe
commit
5afe2d6cb4
285
java/spring/aop/aop模板/权限校验/SSM权限校验.md
Normal file
285
java/spring/aop/aop模板/权限校验/SSM权限校验.md
Normal file
@ -0,0 +1,285 @@
|
||||
# SSM注解+AOP实现鉴权(功能鉴权+数据鉴权)
|
||||
|
||||
## 开启配置
|
||||
|
||||
```xml
|
||||
##设定扫描路径确定能够扫描到后续我们自定义AOP类
|
||||
<context:component-scan base-package="com.b2b.service,com.b2b.task,com.b2b.controller,com.b2b.common.db,com.b2b.common.base.verify" />
|
||||
##开启自动代理 不开启这个的话后续注解在家接口上也是不会生效的
|
||||
<aop:aspectj-autoproxy/>
|
||||
```
|
||||
|
||||
## 自定义权限注解
|
||||
|
||||
### 属性介绍
|
||||
|
||||
- **perm(功能权限)**
|
||||
|
||||
可以看到我这里功能权限的字段用的是int类型的数组,这是因为我当前系统下权限是存在一个长字符串中的 这个字符串的每一个位置代表一个权限,比如新增订单在这个字符串的第二位那么给新增订单添加数据权限perm就应该等于2,代表着是去找这个用户的权限字符串中的第二位是否是1,是1的话代表拥有这个权限,我这里用数组是可以使一个接口在拥有多个权限下的情况才能调用,此处如果不需要可以直接改成int,不过通常用户权限列表不会使用一个长字符串来保存,而是一个字符串类型的集合,每一个权限是一个字符串,一般可以直接用接口的path作为权限名,或者另取别名作为权限名;数据校验的时候就是通过AOP校验传入的权限字符串在他本身权限列表中是否存在,如果不存在就代表他没有权限,需要给他拦截掉不能完成当前的请求
|
||||
|
||||
- **dataPerm(数据权限)**
|
||||
|
||||
`dataPerm`是数据权限,用于校验当前数据是否可以进行修改,比如一个列表,查看一个列表是一个功能权限,删除列表中的一个数据也是一个功能权限,但是有些数据是指定人才可以删除,`dataPerm`就是针对这些特殊数据设定的权限
|
||||
|
||||
- **shopNoParam**
|
||||
|
||||
配合`dataPerm`实现数据权限控制,上面的`dataPerm`用于判断是否开启了数据权限,如果开启了数据权限,那么我们就要验证操作的数据是否可以被当前用户所操控,`shopNoParam`就是用于获取当前操作数据,此字段指定到方法的某个参数为目标,从这个目标上获取到门店号或者门店ID
|
||||
|
||||
- **shopNoParamHandler**
|
||||
|
||||
通过`shopNoParam`获取到了目标对象,但是这个对象还不是我们想要的值,我们想要的值在这个对象里面,这个时候我们需要指定一个处理器,这个处理器用来解析这个对象中的值
|
||||
|
||||
```java
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AuthorityVerify {
|
||||
|
||||
//功能权限
|
||||
int[] perm() default {};
|
||||
|
||||
//数据权限
|
||||
int dataPerm() default -1;
|
||||
|
||||
/**
|
||||
* 门店类数据权限校验 如果不为空则代表开启门店权限校验 会验证当前操作店铺是否拥有操作权限 此权限只有在dataPerm开启时才会生效
|
||||
* @return 获取验证门店的字段
|
||||
*/
|
||||
String shopNoParam() default "";
|
||||
|
||||
/**
|
||||
* ApiParamHandler为抽象模板 不可以直接作为参数处理器使用 应选择其子类作为处理类
|
||||
*/
|
||||
ParamHandlerEnum shopNoParamHandler() default ParamHandlerEnum.OBJECT;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 解析处理类
|
||||
|
||||
```java
|
||||
/**
|
||||
* 解析处理类枚举,注册了两个不同的处理对象,可以直接试用这个枚举进行调用
|
||||
* @author dss
|
||||
* @since 2022/3/9
|
||||
*/
|
||||
public enum ParamHandlerEnum{
|
||||
|
||||
|
||||
OBJECT(1,"",new ObjectParamHandler())
|
||||
,GET_TARGET_PARAM(2,"",new GetTargetParamHandler())
|
||||
|
||||
;
|
||||
|
||||
private final Integer code;
|
||||
private final String name;
|
||||
private final ApiParamHandler<?> handler;
|
||||
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ApiParamHandler<?> getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
ParamHandlerEnum(Integer code, String name, ApiParamHandler<?> handler) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.handler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理器抽象模板
|
||||
* @author dss
|
||||
* @since 2022/8/1
|
||||
*/
|
||||
public abstract class ApiParamHandler<T>{
|
||||
|
||||
protected T pathParam = null;
|
||||
protected T param = null;
|
||||
|
||||
//获取目标参数对象
|
||||
protected void getParam(String tarParamName, String[] paramsSerial,Object[] argsSerial){
|
||||
|
||||
for (int i = 0; i < paramsSerial.length; i++) {
|
||||
if (argsSerial[i]!=null){
|
||||
PathVariable pathAnno = argsSerial[i].getClass().getAnnotation(PathVariable.class);
|
||||
if (pathAnno!=null&&tarParamName.equals(pathAnno.value())){
|
||||
pathParam= (T) argsSerial[i];
|
||||
}
|
||||
if (tarParamName.equals(paramsSerial[i])){
|
||||
param= (T) argsSerial[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将目标对象转换为String类型
|
||||
protected abstract String targetToString(String tarParamName, String[] paramsSerial,Object[] argsSerial);
|
||||
|
||||
// 清空参数
|
||||
protected void clearParam(){
|
||||
pathParam=null;
|
||||
param=null;
|
||||
}
|
||||
|
||||
public synchronized String getTargetStr(String tarParamName, String[] paramsSerial,Object[] argsSerial){
|
||||
getParam(tarParamName, paramsSerial,argsSerial);
|
||||
String s = targetToString(tarParamName, paramsSerial, argsSerial);
|
||||
clearParam();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的处理对象,直接将对象toString,适用于传的参数本身就是要取的值,比如一个接口传入的直接就是门店号,我就不需要再去解析他了
|
||||
* @author dss
|
||||
* @since 2022/8/1
|
||||
* Object 类型参数处理器
|
||||
*/
|
||||
public class ObjectParamHandler extends ApiParamHandler<Object> {
|
||||
|
||||
@Override
|
||||
public String targetToString(String tarParamName, String[] paramsSerial,Object[] argsSerial){
|
||||
|
||||
if (pathParam!=null){
|
||||
return pathParam.toString();
|
||||
}
|
||||
if (param!=null){
|
||||
return param.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface GetTargetParamAble {
|
||||
String getTargetId(); //获取目标值
|
||||
}
|
||||
/**
|
||||
* 自定义解析处理类,传入的对象需要实现GetTargetParamAble接口,这样就可以通过这个接口的getTargetId方法获取到目标值
|
||||
* @author dss
|
||||
* @since 2023
|
||||
* LogTargetAble GetTargetParamAble
|
||||
*/
|
||||
public class GetTargetParamHandler extends ApiParamHandler<GetTargetParamAble> {
|
||||
@Override
|
||||
protected String targetToString(String tarParamName, String[] paramsSerial, Object[] argsSerial) {
|
||||
if (pathParam!=null){
|
||||
return pathParam.getTargetId();
|
||||
}
|
||||
if (param!=null){
|
||||
return param.getTargetId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
### 切面类
|
||||
|
||||
```java
|
||||
@Aspect
|
||||
@Component
|
||||
public class AuthorityVerifyAspect {
|
||||
|
||||
private static final DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
Logger logger = Logger.getLogger(AuthorityVerifyAspect.class);
|
||||
|
||||
//设置切点为AuthorityVerify注解
|
||||
@Pointcut(value = "@annotation(authorityVerify)")
|
||||
public void pointcut(AuthorityVerify authorityVerify) {
|
||||
|
||||
}
|
||||
|
||||
//对切点进行环绕增强
|
||||
@Around(value = "pointcut(authorityVerify)")
|
||||
public Object doAround(ProceedingJoinPoint joinPoint, AuthorityVerify authorityVerify) throws Throwable {
|
||||
|
||||
ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
|
||||
HttpServletRequest request = attribute.getRequest();
|
||||
HttpServletResponse response = attribute.getResponse();
|
||||
|
||||
SysUser sysUser = (SysUser) request.getSession().getAttribute("back_sysUser");
|
||||
List<String> shopNos = (List<String>) request.getSession().getAttribute("shopNos");
|
||||
|
||||
String perm = (String) request.getSession().getAttribute("perm");
|
||||
String dataPerm = (String) request.getSession().getAttribute("dataPerm");
|
||||
|
||||
//用户未登录重定向到登录页面
|
||||
if (sysUser == null) {
|
||||
response.sendRedirect(request.getContextPath()+"/index.shtml?loginFlag=noLogin");
|
||||
}
|
||||
|
||||
//验证功能权限、功能权限不存在重定向到没有权限页面
|
||||
int[] perms = authorityVerify.perm();
|
||||
if (perms!=null&&perms.length>0){
|
||||
logger.debug("进行功能校验");
|
||||
for (int s : perms) {
|
||||
if (perm==null|| perm.charAt(s - 1) == '0'){
|
||||
response.sendRedirect(request.getContextPath()+"/noPerm.shtml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//验证是否开启了数据校验 如果开启了数据权限则才会触发数据权限的附加验证 比如验证是否拥有门店的操作权限
|
||||
int dataP = authorityVerify.dataPerm();
|
||||
if (dataP!=-1 && dataPerm!=null && dataPerm.charAt(dataP-1)=='1'){
|
||||
logger.debug("进行数据校验");
|
||||
Method method = ((MethodSignature) (joinPoint.getSignature())).getMethod();
|
||||
// 形参数组
|
||||
String[] paramsSerial = nameDiscoverer.getParameterNames(method);
|
||||
// 实参数组
|
||||
Object[] argsSerial = joinPoint.getArgs();
|
||||
//验证门店权限
|
||||
//tarParam不为空 则获取目标参数值
|
||||
if (!StringUtils.isEmpty(authorityVerify.shopNoParam())){
|
||||
if (paramsSerial!=null&¶msSerial.length!=0 && authorityVerify.shopNoParamHandler()!=null&&shopNos!=null){
|
||||
ApiParamHandler<?> apiParamHandler = authorityVerify.shopNoParamHandler().getHandler();
|
||||
String targetStr = apiParamHandler.getTargetStr(authorityVerify.shopNoParam(), paramsSerial, argsSerial);
|
||||
if (StringUtils.isEmpty(targetStr)){
|
||||
//跳转到没有权限页面
|
||||
response.sendRedirect(request.getContextPath()+"/noPerm.shtml");
|
||||
}
|
||||
if (!shopNos.contains(targetStr)) {
|
||||
//跳转到没有权限页面
|
||||
response.sendRedirect(request.getContextPath()+"/noPerm.shtml");
|
||||
}
|
||||
}else{
|
||||
//解析参数出错 导致权限校验失败
|
||||
//跳转到没有权限页面
|
||||
response.sendRedirect(request.getContextPath()+"/noPerm.shtml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用
|
||||
|
||||
```java
|
||||
@RequestMapping("/test")
|
||||
@AuthorityVerify(perm = 69,dataPerm = 69,shopNoParam = "shop",shopNoParamHandler = ParamHandlerEnum.GET_TARGET_PARAM)
|
||||
public Map<String,Object> test(StShop shop,String token,Model model){
|
||||
return new HashMap<>();
|
||||
}
|
||||
@RequestMapping("/test2")
|
||||
@AuthorityVerify(perm = 70,dataPerm = 70,shopNoParam = "shopNo",shopNoParamHandler = ParamHandlerEnum.OBJECT)
|
||||
public Map<String,Object> test2(String shopNo){
|
||||
return new HashMap<>();
|
||||
}
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user