spring boot+redis进行接口鉴权

公司老项目,之前是用前端进行资源鉴权,存在很大的风险和问题,最近安全漏洞检测告警好多次,准备对后端接口进行鉴权,于是进行改造。

大致设计:一个用户登录,先把他所有权限信息查出来放进redis,自定义注解、要进行权限控制的接口打上自己的权限注解,如果redis找得到权限信息就放行,找不到则返回无权限状态码:503

1、数据库表设计

  

2、自定义权限注解,方便对需要进行权限判断的接口打上标记

package com.yxkj.common.core.access;

import java.lang.annotation.*;

/**
 * @author xiongxinyan
 * @Description 每个接口方法上的权限控制注解
 * @date 2023/4/17 9:42
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented     // 在生成javac时显示该注解的信息
@Inherited
public @interface Access {
    /**
     * 用于配置具体接口的权限值
     * 在数据库中添加对应的记录
     * 用户登录时,将用户所有的权限列表放入redis中
     * 用户访问接口时,将对应接口的值和redis中的匹配看是否有访问权限
     * 用户退出登录时,清空redis中对应的权限缓存
     */
    String value() default "";
}

3、自定义拦截器

package com.yxkj.common.core.access;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yxkj.common.core.common.BussinessException;
import com.yxkj.common.core.common.GenericController;
import com.yxkj.common.core.config.RedisService;
import com.yxkj.common.core.config.SpringContextUtils;
import com.yxkj.common.core.entity.role.InterfacePermissionsVo;
import com.yxkj.common.core.entity.sys.SysPara;
import com.yxkj.common.core.mapper.InterfacePermissionsRoleMapper;
import com.yxkj.common.core.service.ISysParaService;
import com.yxkj.common.core.util.common.UserUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author xiongxinyan
 * @Description 自定义权限拦截器Interceptor
 * @date 2023/4/17 9:49
 */
@Component
public class AccessInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response1, Object handler) throws Exception {

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Access access = method.getAnnotation(Access.class);
        if (access == null) {
            // 如果注解为null, 说明不需要拦截, 直接放过
            return true;
        }
        RedisService redisService = getBean(RedisService.class, request);
        String userId = UserUtils.getUserID();
        String Access = "access:" + userId;
        // 有注解,验证该用户是否有该接口权限
        // 读取redis中缓存该用户权限进行判
        String s = redisService.get(Access);
        ISysParaService iSysParaService = SpringContextUtils.getBean(ISysParaService.class);
        // Java过期时间
        SysPara systokenTime = iSysParaService.getSysPara("Access_Token_Time");
        if (null == s) {
        // 为空则代表redis内不存在,从数据库获取
            InterfacePermissionsRoleMapper interfacePermissionsRoleMapper = getBean(InterfacePermissionsRoleMapper.class, request);

            List<InterfacePermissionsVo> interfacePermissionsVos = interfacePermissionsRoleMapper.selectInterfaceRole(userId);
            if (interfacePermissionsVos.size() == 0) {
        // 数据库不存在则塞一个默认值
                redisService.put(Access, "0",(int) (Double.parseDouble(systokenTime.getParaValue()) * 60));
                GenericController.getResponse().setStatus(503);
                HttpServletResponse response = GenericController.getResponse();
                response.setStatus(503);
                throw new BussinessException(BussinessException.Code.E503);
            } else {
        // 若存在则判断是否存在该权限
                StringBuilder accessAll = new StringBuilder();
                for (InterfacePermissionsVo interfacePermissionsVo : interfacePermissionsVos) {
                    accessAll.append(interfacePermissionsVo.getInterfaceUrl()).append(":").append(interfacePermissionsVo.getInterfaceRequest()).append(",");
                }
                redisService.put(Access, accessAll.toString(),(int) (Double.parseDouble(systokenTime.getParaValue()) * 60));
                //redis没有则读数据库存到redis中
                // 返回是否存在的Boolean值
                return accessAll.toString().contains(access.value());
            }
        }
        // 不存在则返回503无权限状态码
        if (!s.contains(access.value())) {
            GenericController.getResponse().setStatus(503);
            HttpServletResponse response = GenericController.getResponse();
            response.setStatus(503);
            throw new BussinessException(BussinessException.Code.E503);
        }
        return s.contains(access.value());
    }

    public <T> T getBean(Class<T> clazz, HttpServletRequest request) {
        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        return applicationContext.getBean(clazz);
    }
}

4、注册拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AccessInterceptor()).addPathPatterns("/**");
    }

5、修改权限过后清除redis缓存,解决缓存不一致问题

    /**
     * 删除redis下用户接口权限缓存
     */
    private void deleteRedis() {
        // 获取Redis中特定前缀
        Set<String> keys = redisTemplate.keys("access:" + "*");
        // 删除
        redisTemplate.delete(keys);
    }

6、对需要鉴权的接口加上注解

7、在数据库添加用户信息和接口权限即可

8、具体有无权限会出现如下页面

有权限:

 

无权限:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值