当前位置:   article > 正文

Springboot实现RBAC权限校验_springboot rbac

springboot rbac

目录

RBAC思想

实现方式

一图流实现思路

代码实现

 导入相关依赖

实现登录与用户的token携带

编写登录controller接口

编写登录service业务

实现登录业务

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

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

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

在切面中编写通知

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

使用postman测试

登录测试

正常登录

 密码或用户名有误

 token拦截测试

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

使用错误token

 使用正常token但无访问权限


RBAC思想

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

实现方式

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

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

一图流实现思路

代码实现

 导入相关依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-test</artifactId>
  8. <scope>test</scope>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.projectlombok</groupId>
  12. <artifactId>lombok</artifactId>
  13. <version>1.16.12</version>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.aspectj</groupId>
  17. <artifactId>aspectjrt</artifactId>
  18. <version>1.8.9</version>
  19. </dependency>
  20. <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjtools -->
  21. <dependency>
  22. <groupId>org.aspectj</groupId>
  23. <artifactId>aspectjtools</artifactId>
  24. <version>1.8.9</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.aspectj</groupId>
  28. <artifactId>aspectjweaver</artifactId>
  29. <version>1.7.4</version>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-starter-data-redis</artifactId>
  34. </dependency>
  35. <!--hutool-->
  36. <dependency>
  37. <groupId>cn.hutool</groupId>
  38. <artifactId>hutool-all</artifactId>
  39. <version>5.7.17</version>
  40. </dependency>
  41. <dependency>
  42. <groupId>com.alibaba</groupId>
  43. <artifactId>fastjson</artifactId>
  44. <version>1.2.62</version>
  45. </dependency>
  46. <!--hutool-->
  47. <dependency>
  48. <groupId>cn.hutool</groupId>
  49. <artifactId>hutool-all</artifactId>
  50. <version>5.7.17</version>
  51. </dependency>

实现登录与用户的token携带

编写登录controller接口

  1. package com.melody.rest.restcontroller;
  2. import com.melody.rest.domain.RestSysUser;
  3. import com.melody.rest.model.ResultJson;
  4. import com.melody.rest.service.RestAuthService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.PostMapping;
  7. import org.springframework.web.bind.annotation.RequestBody;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. @RestController
  11. @RequestMapping("/rest")
  12. public class LoginController {
  13. @Autowired
  14. private RestAuthService restAuthService;
  15. //登录
  16. @PostMapping("/login")
  17. public ResultJson index(@RequestBody RestSysUser restSysUser){
  18. //登录以及登录成功存入token
  19. return restAuthService.Login(restSysUser);
  20. }
  21. }

编写登录service业务

  1. package com.melody.rest.service;
  2. import com.melody.rest.domain.RestSysUser;
  3. import com.melody.rest.model.ResultJson;
  4. public interface RestAuthService {
  5. //登录方法
  6. ResultJson Login(RestSysUser restSysUser);
  7. }

实现登录业务

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

  1. package com.melody.rest.service.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.bean.copier.CopyOptions;
  4. import com.melody.rest.domain.RestSysUser;
  5. import com.melody.rest.model.ResultCode;
  6. import com.melody.rest.model.ResultJson;
  7. import com.melody.rest.service.RestAuthService;
  8. import com.melody.rest.util.ResourceVerification;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.data.redis.core.RedisTemplate;
  11. import org.springframework.stereotype.Service;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. import java.util.UUID;
  15. import java.util.concurrent.TimeUnit;
  16. @Service
  17. public class RestAuthServiceImpl implements RestAuthService {
  18. @Autowired
  19. RedisTemplate redisTemplate;
  20. @Override
  21. public ResultJson Login(RestSysUser restSysUser) {
  22. //账号密码校验,
  23. if("admin".equals(restSysUser.getUsername()) && "123456".equals(restSysUser.getPassword())){
  24. //账号密码正确
  25. restSysUser.setResources(ResourceVerification.resource());//向用户权限中注入写死的权限
  26. Map<String, Object> userMap = BeanUtil.beanToMap(restSysUser, new HashMap<>(),
  27. CopyOptions.create()
  28. .setIgnoreNullValue(true)//忽略一些空值
  29. .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
  30. UUID uuid = UUID.randomUUID();
  31. String tokenKey= String.valueOf(uuid);
  32. String token="LoginUserKey "+tokenKey;
  33. //存储
  34. redisTemplate.opsForHash().putAll(token,userMap);
  35. //设置存值时间,expire默认秒,改为天数,设置1天
  36. redisTemplate.expire(token,1, TimeUnit.DAYS);
  37. return ResultJson.ok(token);
  38. }else{
  39. //账号密码不正确
  40. return ResultJson.failure(ResultCode.LOGIN_ERROR);
  41. }
  42. }
  43. }

测试用的权限:

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

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

拦截器不受spring管理,因此需要在拦截器中注入redis模板类RedisTemplate,使用有参构造将RedisTemplate传递给token拦截类

  1. package com.melody.rest.config;
  2. import com.melody.rest.util.LoginInterceptor;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.data.redis.core.RedisTemplate;
  5. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  6. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  7. import javax.annotation.Resource;
  8. @Configuration
  9. public class MvcConfig implements WebMvcConfigurer {
  10. @Resource
  11. RedisTemplate redisTemplate;
  12. @Override
  13. public void addInterceptors(InterceptorRegistry registry) {
  14. //配置登录查看是否有token拦截器
  15. registry.addInterceptor(new LoginInterceptor(redisTemplate)).addPathPatterns("/testRest/**").order(0);
  16. }
  17. }
  1. package com.melody.rest.util;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.melody.rest.domain.RestData;
  5. import com.melody.rest.domain.RestSysUser;
  6. import org.springframework.data.redis.core.RedisTemplate;
  7. import org.springframework.web.servlet.HandlerInterceptor;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.util.Map;
  11. public class LoginInterceptor implements HandlerInterceptor {
  12. private RedisTemplate redisTemplate;
  13. public LoginInterceptor(RedisTemplate redisTemplate){
  14. this.redisTemplate=redisTemplate;
  15. }
  16. @Override
  17. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  18. //设置编码
  19. response.setCharacterEncoding("utf-8");
  20. response.setContentType("text/json;charset=utf-8");
  21. //1、判断是否携带token
  22. String token = request.getHeader("authorization");
  23. if(token==null || "".equals(token)){
  24. RestData restData = RestData.builder().code("401").msg("请先登录再操作!").build();
  25. String jsonRestData = JSONObject.toJSONString(restData);
  26. response.setStatus(401);
  27. response.getWriter().write(jsonRestData);
  28. return false;
  29. }
  30. Map<String, Object> userMap=redisTemplate.opsForHash().entries(token);
  31. RestSysUser restSysUser = BeanUtil.fillBeanWithMap(userMap, new RestSysUser(), false);
  32. //2、判断redis里面是否存在token
  33. if(userMap.isEmpty()){
  34. RestData restData = RestData.builder().code("401").msg("请先登录再操作!").build();
  35. String jsonRestData = JSONObject.toJSONString(restData);
  36. response.setStatus(401);
  37. response.getWriter().write(jsonRestData);
  38. return false;
  39. }
  40. return true;
  41. }
  42. }

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

  1. package com.melody.rest.annotion;
  2. import java.lang.annotation.*;
  3. @Target({ ElementType.PARAMETER, ElementType.METHOD })
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Documented
  6. public @interface AuthCheck {
  7. public String value() default "";
  8. }

在切面中编写通知

  1. package com.melody.rest.aspect;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import com.melody.rest.annotion.AuthCheck;
  4. import com.melody.rest.domain.RestSysUser;
  5. import com.melody.rest.exception.AuthException;
  6. import com.melody.rest.model.ResCode;
  7. import com.melody.rest.model.ResJson;
  8. import com.melody.rest.model.ResultCode;
  9. import com.melody.rest.model.ResultJson;
  10. import org.aspectj.lang.ProceedingJoinPoint;
  11. import org.aspectj.lang.annotation.Around;
  12. import org.aspectj.lang.annotation.Aspect;
  13. import org.aspectj.lang.annotation.Pointcut;
  14. import org.aspectj.lang.reflect.MethodSignature;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.beans.factory.annotation.Value;
  17. import org.springframework.data.redis.core.RedisTemplate;
  18. import org.springframework.stereotype.Component;
  19. import javax.annotation.Resource;
  20. import javax.servlet.http.HttpServletRequest;
  21. import java.lang.reflect.Method;
  22. import java.util.Map;
  23. @Aspect
  24. @Component
  25. public class AuthAspect {
  26. @Value("${token.header}")
  27. private String header;
  28. @Autowired
  29. private HttpServletRequest request;
  30. @Resource
  31. private RedisTemplate redisTemplate;
  32. @Pointcut("@annotation(com.melody.rest.annotion.AuthCheck)")
  33. public void authPointCut(){
  34. }
  35. @Around("authPointCut()")
  36. public Object authCheck(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  37. try{
  38. // 判断 TOKEN
  39. String token = request.getHeader(header);
  40. System.err.println(token);
  41. Map<String, Object> userMap=redisTemplate.opsForHash().entries(token);
  42. RestSysUser restSysUser = BeanUtil.fillBeanWithMap(userMap, new RestSysUser(), false);
  43. if(restSysUser.getUsername() == null || restSysUser.getUsername().equals("")){
  44. throw new AuthException(ResCode.TOKEN_NOT_EXIST);
  45. } else {
  46. if(restSysUser.getResources()==null){
  47. throw new AuthException(ResCode.BANED_REQUEST);
  48. }
  49. //从切面织入点处通过反射机制获取织入点处的方法
  50. MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
  51. //获取切入点所在的方法
  52. Method method = signature.getMethod();
  53. AuthCheck ac = method.getAnnotation(AuthCheck.class);
  54. // System.err.println("=========================="+ac);
  55. boolean flag = false;
  56. if(ac != null) {
  57. //获取切入点方法的value值,该测试接口设置的value为权限字段
  58. String auth = ac.value();
  59. // System.err.println("-----------------------------"+auth);
  60. flag = restSysUser.getResources().stream().anyMatch(str -> str.equals(auth));
  61. // way2:数据库中存放权限字段,根据注解的value确定请求所需权限判断是否有权限进行访问
  62. }
  63. if(!flag) {
  64. return ResultJson.failure(ResultCode.FORBIDDEN);
  65. }
  66. }
  67. } catch(AuthException e) {
  68. System.err.println(e.getResCode().getCode() + ":" + e.getResCode().getMsg());
  69. return ResJson.no(e.getResCode());
  70. }
  71. // Object res = proceedingJoinPoint.proceed();
  72. return proceedingJoinPoint.proceed();
  73. }
  74. }

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

  1. package com.melody.rest.restcontroller;
  2. import com.melody.rest.annotion.AuthCheck;
  3. import com.melody.rest.model.ResultJson;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. @RestController
  8. @RequestMapping("/testRest")
  9. public class TestRestController {
  10. //测试方法1
  11. @AuthCheck("/testRest/t1")
  12. @PostMapping("/t1")
  13. public ResultJson test(){
  14. return ResultJson.ok("test1访问成功");
  15. }
  16. //测试方法2
  17. @AuthCheck("/testRest/t10")
  18. @PostMapping("/t10")
  19. public ResultJson test2(){
  20. return ResultJson.ok("test10访问");
  21. }
  22. }

使用postman测试

登录测试

正常登录

 密码或用户名有误

 token拦截测试

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

使用错误token

 

 使用正常token但无访问权限

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

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号