Springboot实现RBAC权限校验

目录

RBAC思想

实现方式

一图流实现思路

代码实现

 导入相关依赖

实现登录与用户的token携带

编写登录controller接口

编写登录service业务

实现登录业务

实现登录后操作的权限验证

 实现token拦截器,对所有操作进行身份验证

自定义注解,作为权限验证的切入点

在切面中编写通知

编写测试接口,测试登录后的用户操作

使用postman测试

登录测试

正常登录

 密码或用户名有误

 token拦截测试

使用后正常登录后获取的token

使用错误token

 使用正常token但无访问权限


RBAC思想

        RBAC基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限。这样做的好处是,不必在每次创建用户时都进行分配权限的操作,只要分配用户相应的角色即可,而且角色的权限变更比用户的权限变更要少得多,这样将简化用户的权限管理,减少系统的开销。

实现方式

Springboot+AOP切面+自定义注解+redis+jwt+mybatis+token拦截器

注:为了简化操作和流程,没有真正地从mysql中读取数据,而是以写死的数据进行演示

一图流实现思路

代码实现

 导入相关依赖

        <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>

实现登录与用户的token携带

编写登录controller接口

package com.melody.rest.restcontroller;

import com.melody.rest.domain.RestSysUser;
import com.melody.rest.model.ResultJson;
import com.melody.rest.service.RestAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @Autowired
    private RestAuthService restAuthService;


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

}

编写登录service业务

package com.melody.rest.service;


import com.melody.rest.domain.RestSysUser;
import com.melody.rest.model.ResultJson;

public interface RestAuthService {

    //登录方法
    ResultJson Login(RestSysUser restSysUser);
    

}

实现登录业务

在业务中判断账号密码的正确性,并将用户账户、密码、token存入redis,设置token一天有效期

package com.melody.rest.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.melody.rest.domain.RestSysUser;
import com.melody.rest.model.ResultCode;
import com.melody.rest.model.ResultJson;
import com.melody.rest.service.RestAuthService;
import com.melody.rest.util.ResourceVerification;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;


import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


@Service
public class RestAuthServiceImpl implements RestAuthService {

    @Autowired
    RedisTemplate redisTemplate;


    @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,1, TimeUnit.DAYS);
            return ResultJson.ok(token);
        }else{
            //账号密码不正确
            return ResultJson.failure(ResultCode.LOGIN_ERROR);
        }
    }
}

测试用的权限:

实现登录后操作的权限验证

 实现token拦截器,对所有操作进行身份验证

拦截器不受spring管理,因此需要在拦截器中注入redis模板类RedisTemplate,使用有参构造将RedisTemplate传递给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");
        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;
        }

        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}")
    private String header;

    @Autowired
    private HttpServletRequest request;

    @Resource
    private RedisTemplate redisTemplate;

    @Pointcut("@annotation(com.melody.rest.annotion.AuthCheck)")
    public void authPointCut(){

    }


    @Around("authPointCut()")
    public Object authCheck(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try{
            // 判断 TOKEN
            String token = request.getHeader(header);
            System.err.println(token);
            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);
//                System.err.println("=========================="+ac);
                boolean flag = false;
                if(ac != null) {
                    //获取切入点方法的value值,该测试接口设置的value为权限字段
                    String auth = ac.value();
//                    System.err.println("-----------------------------"+auth);
                    flag = restSysUser.getResources().stream().anyMatch(str -> str.equals(auth));
                    // way2:数据库中存放权限字段,根据注解的value确定请求所需权限判断是否有权限进行访问
                }
                if(!flag) {
                    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 proceedingJoinPoint.proceed();
    }

}

编写测试接口,测试登录后的用户操作

package com.melody.rest.restcontroller;

import com.melody.rest.annotion.AuthCheck;
import com.melody.rest.model.ResultJson;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;



@RestController
@RequestMapping("/testRest")
public class TestRestController {



    //测试方法1
    @AuthCheck("/testRest/t1")
    @PostMapping("/t1")
    public ResultJson test(){
        return ResultJson.ok("test1访问成功");
    }

    //测试方法2
    @AuthCheck("/testRest/t10")
    @PostMapping("/t10")
    public ResultJson test2(){
        return ResultJson.ok("test10访问");
    }

}

使用postman测试

登录测试

正常登录

 密码或用户名有误

 token拦截测试

使用后正常登录后获取的token

使用错误token

 

 使用正常token但无访问权限

在写死的权限中只有test1、2、3的权限,而方法二权限为test10

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RBAC权限管理系统是基于角色的用户权限控制系统。在springboot中,我们可以使用Spring Security框架来实现RBAC权限管理。Spring Security是一个功能强大的、灵活的框架,可以帮助我们在应用程序中实现身份验证、授权和其他安全功能。 要在springboot中使用RBAC权限管理系统,可以按照以下步骤进行操作: 1. 导入Spring Security依赖:在pom.xml文件中添加Spring Security相关的依赖项。 2. 配置Spring Security:在应用程序的配置文件中,设置Spring Security的基本参数,例如认证方式、登录页面等。 3. 创建用户和角色实体:使用实体类表示用户和角色,并建立它们之间的关系。 4. 实现用户认证和授权:通过自定义的用户认证和授权逻辑,实现用户登录验证和权限控制。可以使用注解或配置文件的方式进行权限配置。 5. 创建页面和接口:根据系统需求,创建相应的页面和接口,并为每个角色分配不同的权限。 6. 完善系统功能:根据实际需求,完善RBAC权限管理系统的其他功能,例如日志记录、异常处理等。 需要注意的是,以上步骤仅为概括性的指导,实际操作可能会因具体需求而有所不同。可以根据具体的项目情况进行适当的调整和扩展。 总结起来,springboot中的RBAC权限管理是通过使用Spring Security框架来实现的,需要进行依赖导入、配置、实体创建、认证和授权逻辑实现、页面和接口创建等步骤。通过这些步骤,可以构建一个完善的RBAC权限管理系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值