SpringBoot自定注解实现权限管理(项目实现)

1、简介

本文章讲解的是基于RBAC模型的数据库设计的拦截器+自定义注解的形式实现权限验证 

RBAC的数据库设计看:关于使用RBAC模型,实现用户权限管理_&波吉&的博客-CSDN博客

2、导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjtools -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.17</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.17</version>
        </dependency>

登入的controller层

@RestController
@RequestMapping("/rest")
public class LoginController {

    @Autowired
    private RestAuthService restAuthService;


    //登录
    @PostMapping(value = "/login")
    public ResultJson index(@RequestBody RestSysUser restSysUser){
        //登录以及登录成功存入token
        return restAuthService.Login(restSysUser);
    }

}

service层(主要的作用是获取用户相应的token,并存入redis)

    @Override
    public ResultJson Login(RestSysUser restSysUser) {
        //账号密码校验,
        if("admin".equals(restSysUser.getUsername()) && "123456".equals(restSysUser.getPassword())){
            //账号密码正确
            //登录成功
            restSysUser.setResources(ResourceVerification.resource());
            Map<String, Object> userMap = BeanUtil.beanToMap(restSysUser, new HashMap<>(),
                    CopyOptions.create()
                            .setIgnoreNullValue(true)//忽略一些空值
                            .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
            UUID uuid = UUID.randomUUID();
            String tokenKey= String.valueOf(uuid);
            String token="LoginUserKey "+tokenKey;
            //存储
            redisTemplate.opsForHash().putAll(token,userMap);
            //设置存值时间,expire默认秒:1天
            redisTemplate.expire(token,60*60*24, TimeUnit.MINUTES);
            return ResultJson.ok(token);
        }else{
            //账号密码不正确
            return ResultJson.failure(ResultCode.LOGIN_ERROR);
        }
    }

拦截器的配置(拦截了所有的请求并,验证是否存在token以及是否过期)

package com.melody.rest.config;

import com.melody.rest.util.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


import javax.annotation.Resource;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Resource
    RedisTemplate redisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //配置登录查看是否有token拦截器
        registry.addInterceptor(new LoginInterceptor(redisTemplate)).addPathPatterns("/testRest/**").order(0);


    }
}

package com.melody.rest.util;


import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.melody.rest.domain.RestData;
import com.melody.rest.domain.RestSysUser;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

public class LoginInterceptor implements HandlerInterceptor {

    private RedisTemplate redisTemplate;

    public LoginInterceptor(RedisTemplate redisTemplate){
        this.redisTemplate=redisTemplate;
    }


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //设置编码
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/json;charset=utf-8");

        //1、判断是否携带token
        String token = request.getHeader("authorization");
        System.out.println(token);
        if(token==null || "".equals(token)){
            RestData restData = RestData.builder().code("401").msg("你未登录").build();
            String jsonRestData = JSONObject.toJSONString(restData);
            response.setStatus(401);
            response.getWriter().write(jsonRestData);
            return false;
        }
        Map<String, Object> userMap=redisTemplate.opsForHash().entries(token);
        RestSysUser restSysUser = BeanUtil.fillBeanWithMap(userMap, new RestSysUser(), false);
        //2、判断redis里面是否存在token
        if(userMap.isEmpty()){
            RestData restData = RestData.builder().code("401").msg("你未登录").build();
            String jsonRestData = JSONObject.toJSONString(restData);
            response.setStatus(401);
            response.getWriter().write(jsonRestData);
            return false;
        }

        //3、判断账号情况
        if(restSysUser.getUsername()!=null){
            //获取数据库账号情况
            //比对,如果账号异常,则不能访问
            //return false;
        }
        return true;
    }
}

接下来,就是该用户是否有权限访问

自定义注解的形式

package com.melody.rest.annotion;

import java.lang.annotation.*;

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthCheck {

    public String value() default "";

}

切面的方式(环绕的方式返回验证结果)

package com.melody.rest.aspect;


import cn.hutool.core.bean.BeanUtil;
import com.melody.rest.annotion.AuthCheck;
import com.melody.rest.domain.RestSysUser;
import com.melody.rest.exception.AuthException;
import com.melody.rest.model.ResCode;
import com.melody.rest.model.ResJson;
import com.melody.rest.model.ResultCode;
import com.melody.rest.model.ResultJson;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;

@Aspect
@Component
public class AuthAspect {

    @Value("${token.header}")
    //@Value("authorization")
    private String header;

    @Autowired
    private HttpServletRequest request;

    @Resource
    private RedisTemplate redisTemplate;

    
     /**
     * 目标方法
     */
    @Pointcut("@annotation(com.melody.rest.annotion.AuthCheck)")
    public void authPointCut(){

    }

   

    /**
     * 目标方法调用之前执行
     */
    @Before("authPointCut()")
    public void doBefore() {
        System.out.println("================== step 2: before ==================");
    }

    /**
     * 目标方法调用之后执行
     */
    @After("authPointCut()")
    public void doAfter() {
        System.out.println("================== step 4: after ==================");
    }

    /**
     * 环绕
     * 会将目标方法封装起来
     * 具体验证业务数据
     */
    @Around("authPointCut()")
    public Object authCheck(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try{
            // 判断 TOKEN
            String token = request.getHeader(header);
            Map<String, Object> userMap=redisTemplate.opsForHash().entries(token);
            RestSysUser restSysUser = BeanUtil.fillBeanWithMap(userMap, new RestSysUser(), false);
            if(restSysUser.getUsername() == null || restSysUser.getUsername().equals("")){
                throw new AuthException(ResCode.TOKEN_NOT_EXIST);
            } else {
                if(restSysUser.getResources()==null){
                    throw new AuthException(ResCode.BANED_REQUEST);
                }
                //从切面织入点处通过反射机制获取织入点处的方法
                MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
                //获取切入点所在的方法
                Method method = signature.getMethod();
                AuthCheck ac = method.getAnnotation(AuthCheck.class);
                boolean flag = false;
                if(ac != null) {
                    String auth = ac.value();
                    flag = restSysUser.getResources().stream().anyMatch(str -> str.equals(auth));
                    // way2:数据库中存放权限字段,根据注解的value确定请求所需权限判断是否有权限进行访问
                }
                if(!flag) {
                    //throw new AuthException(ResCode.BANED_REQUEST);
                    return ResultJson.failure(ResultCode.FORBIDDEN);
                }
            }
        } catch(AuthException e) {
            System.err.println(e.getResCode().getCode() + ":" + e.getResCode().getMsg());
            return ResJson.no(e.getResCode());
        }
        Object res = proceedingJoinPoint.proceed();
        return res;
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值