用aop实现接口权限

前言

提示:拦截器和aop都能实现像shiro和springsecurity的权限控制

一、定义一个切面

@Aspect
@Component
public class PermissionAdvice {
    /**
     * 定义切点方法,普通方法标记该注解的都会被切
     */
    @Pointcut("@annotation(com.shidai.petroleum.anno.PermissionAnnotation)")
    public void permissionCheck(){}

    /**
     *获取token,并检验是否有该方法的权限
     * @param joinPoint 切点的方法入参
     * @return
     * @throws Throwable
     */
    @Around("permissionCheck()")
    public Object permissionCheckSecond(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取请求对象中的token+jwtutil取出该token中权限list,将token中的权限取出,权限列表中如果有该注解值就可以访问该注解
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        assert sra != null;
        HttpServletRequest request = sra.getRequest();
        String token = request.getHeader("token");
        //如果token不为空,然后有效的话,就检测是否满足该切点的权限
        if(token!=null && JwtUtil.verify(token)){
            //获取了权限列表
            String[] permissions = JwtUtil.getPermissions(token);
            //获取被代理类名+方法名
            String className = joinPoint.getTarget().getClass().getSimpleName();
            String methodName = joinPoint.getSignature().getName();
            //获取方法的参数类型-》获取到方法上的注解信息
            Class<?> classTarget = joinPoint.getTarget().getClass();
            Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
            Method declaredMethod = classTarget.getDeclaredMethod(methodName, parameterTypes);
            PermissionAnnotation annotation = declaredMethod.getAnnotation(PermissionAnnotation.class);
            //该注解所需要的权限
            String needpermission = annotation.value();
            System.out.println(className+":"+methodName+":"+needpermission);
            for (String permission : permissions) {
                if(permission.equals(needpermission)){
                    return joinPoint.proceed();
                }
            }
        }
        HttpServletResponse response = sra.getResponse();
        response.setStatus(401);
        response.getOutputStream().write("token有误或者你没有该权限".getBytes(StandardCharsets.UTF_8));
        //设置响应格式,防止中文乱码
        response.setContentType("application/json;charset=utf-8");
        return null;
    }
}

二、定义自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionAnnotation {
    String value();//这个是存储权限的变量
}

三、选择一个被切点,配置自定义注解

@RestController
@RequestMapping("/order")
public class OrderVO {
    @Autowired
    OrderService orderService;

    //该商品的权限只能是货主的add,才能访问
    @PermissionAnnotation("huozu:add")
    @PostMapping("/")
    public InfoResult<Integer> saveOrder(@RequestBody OrderPO orderPO){
        return orderService.saveOrder(orderPO);
    }

    /**
     * 货主只能看到自己的订单,而承运商可以看到所有人的订单
     * @param pageNum
     * @param pageSize
     * @return
     */
    //只有货主的查找权限才行
    @PermissionAnnotation("huozu:find")
    @GetMapping("/{num}/{size}")
    public InfoResult<PageInfo<OrderPO>> orderPage(@PathVariable("num") int pageNum,@PathVariable("size") int pageSize){
        PageHelper.startPage(pageNum,pageSize);
        PageInfo<OrderPO> orderPages = new PageInfo<>(orderService.orderPage());
        return new InfoResult<>(200,orderPages,"查询分页数据成功");
    }
}

四、登录服务中将权限和角色存进token中

给浏览器返回相应头token

@Service
public class UserServiceImpl implements UserService {
    @Resource
    UserMapper userMapper;
    @Resource
    RoleMapper roleMapper;
    @Resource
    PermissionMapper permissionMapper;
    @Override
    public Integer saveUser(UserPO userPO) {
        return userMapper.saveUser(userPO);
    }

    @Override
    public InfoResult<UserPO> loginUser(UserPO userPO) {
        //先去数据库根据账号密码,查到是否有用户对象
        UserPO user = userMapper.loginUser(userPO.getUsername(), userPO.getPassword());
        if(user==null){
            return new InfoResult<UserPO>(401,userPO,"账号或密码错误");
        }else{
            //登录成功,先去查询该账户对应的角色
            RolePO rolePO = roleMapper.findRoleByUid(user.getId());
            String role = rolePO.getName();
            //获取权限
            List<PermissionPO> permissionPOS = permissionMapper.permissionList(rolePO.getId());
            List<String> permissions = new ArrayList<>();
            for (PermissionPO permissionPO : permissionPOS) {
                permissions.add(permissionPO.getName());
            }
            String[] permission = permissions.toArray(new String[permissions.size()]);
            //创建token
            String token = JwtUtil.createToken(user.getId(), permission, role);
            user.setToken(token);
            //将token设置到请求头中
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            assert sra != null;
            HttpServletResponse response = sra.getResponse();
            response.setHeader("token",token);
            //前后端分离需要暴露请求头
            response.setHeader("Access-Control-Expose-Headers", "token");
            return new InfoResult(200,user,"登录成功");
        }
    }
}

五、JwtUtil工具类封装生成token方法

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Arrays;
import java.util.Date;



/**
 * @Author:roger
 * @Date:2022/2/15 11:18:06
 * @Description:
 */
public class JwtUtil {
    private static final String SECRET = "shidai"; // 秘钥
    private static final String ISSUSER = "java"; // 签发人
    private static final long EXPIRES = 30*60*1000; // token的过期时间 30min

    // 用来创建jwt静态方法

    public static String createToken(int uid,String[] permissions,String role){
        //加密算法
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        Date now = new Date();
        Date expires = new Date(now.getTime() + EXPIRES);
        String jwt = JWT.create()
                .withIssuer(ISSUSER)        //签发人
                .withIssuedAt(now)         //签发时间
                .withExpiresAt(expires)     //过期时间
                .withArrayClaim("permission",permissions)  //加入权限
                .withClaim("role",role)                     //加入角色
                .withClaim("uid",uid)                   //加入用户名
                .sign(algorithm);
        return jwt;
    }

    // 校验jwt是否合法
    public static boolean verify(String token) {
        try {
            // 签名算法
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUSER).build();
            verifier.verify(token);
            return true;
        } catch (Exception ex) {
            //ex.printStackTrace();
            return false;
        }
    }

    // 从token中获取用户名
    public static int getUid(String token) {
        try {
            return JWT.decode(token).getClaim("uid").asInt();
        } catch (Exception ex) {
            //ex.printStackTrace();
            return 0;
        }
    }

    // 从token中获取用户的权限数组
    public static String[] getPermissions(String token) {
        try {
            return JWT.decode(token).getClaim("permission").asArray(String.class);
        } catch (Exception ex) {
            //ex.printStackTrace();
            return null;
        }
    }

    //从token中获取用户的角色
    public static String getRole(String token){
        try {
            return JWT.decode(token).getClaim("role").asString();
        } catch (Exception ex) {
            //ex.printStackTrace();
            return null;
        }
    }

    // 判断token是否过期,true-过期,false-没过期
    public static boolean isExpires(String token) {
        Date now = new Date();
        Date expire = JWT.decode(token).getExpiresAt();
        if (now.after(expire)) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) throws InterruptedException {
        String[] permissions = {"vip:add","vip:find"};
        String token=createToken(1,permissions,"vip");
        System.out.println(token);
        Thread.sleep(500);
        System.out.println(verify(token));
        System.out.println(verify("aaa.b.bc"));
        System.out.println(Arrays.toString(getPermissions(token)));
        System.out.println(getRole(token));
        System.out.println(getUid(token));
        System.out.println(isExpires(token));
    }
}

总结

整个流程思路如下
先登录,登录成功就查用户对应的角色权限,通过jwtutils封装进token中,然后设置到相应头中,浏览器接收到后,之后的请求都会携带token请求,当需要访问带权限的请求时,通过aop代理进行增强,在方法执行前,先取出token判断是否有该权限,如果有,就放行,否则不放行

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值