ADD SSM鉴权

This commit is contained in:
unknown 2023-05-20 16:48:46 +08:00
parent 0688283cbe
commit 5afe2d6cb4

View 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&&paramsSerial.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<>();
}
```