一个轻量级的权限框架 基于Springboot,整合了RSA、JWT,使用了RBAC模式。

Au权限框架 基于Springboot的权限认证框架,整合了RSA、JWT。使用了RBAC模式。省去了很多繁琐的配置、使用非常简单。

基于springboot 集成了jwt的权限认证以及RBAC(Role-Based Access Control)多角色控制资源,以及集成了RSA的自动验证参数的功能、同时用quartz自动化来自动刷新的公私秘钥

先看yml配置。

au-config:
  jwt-config:
    ttl: 180d
    key: nidekey
  any-perm-default: any
  forbid-perm-default:
  au-access-perm-default: user
  refresh-time: 5m #rsa密钥对的刷新时间

使用方法

1. 导入jar包

<dependency>
  <groupId>cn.omisheep.au</groupId>
  <artifactId>au-spring-boot-start</artifactId>
  <version>1.1.1</version>
</dependency>

2. 开启Au

用@AuEnable标记主接口

@SpringBootApplication
@AuEnable
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

3. 建立一个User类,继承UserContext类

  • 需要四个属性,id, password , username, role
    • role是角色,比如 user,admin,student
@Data
public class User extends UserContext {
    private Integer id;
    private String password;
    private String username;
    private String role;
}

4. 建立一个数据管理类UserData,实现AuUserData,用于登录和从header中获得User

  • 注意泛型以及@Component注解。

  • 1、实现一个getUser,不用验证身份的获取User,通过id或者username或者自定义属性获得。

  • 2、实现login。身份验证,返回User,如果验证失败,返回null。

@Component
public class UserData implements AuUserData<User> {

    @Override
    public User getUser(Object userId, String username, Claims claims) {
        User user = new User();
        user.setId((Integer) userId);
        user.setUsername(username);
        user.setRole("admin,user,zxc");
        return user;
    }

    @Override
    public User login(String username, String password) {
        if ("zhouxinchen".equals(username) && "123456".equals(password)) {
            User user = new User();
            user.setUsername(username);
            user.setPassword(password);
            user.setRole("admin,user");
            return user;
        } else {
            return null;
        }
    }
}

5. 权限管理

在需要配置权限的地方加上@Au,@AuUser注解即可

@RestController
@RequestMapping("/test")
@Au("test_role")
public class TestController {

    @Au
    @GetMapping("/")
    public String test1() {
        return "成功";
    }
  
    @GetMapping("/")
    public String test2(@AuUser User user) {
        return "成功";
    }

    @Au(exclude = "zxc")
    @GetMapping("/")
    public String test3(@AuUser User user) {
        return "成功";
    }

    @Au("admin")
    @GetMapping("/")
    public String test4(@AuUser(authority = false) User user) {
        return "成功";
    }
}

@Au注解

  1. @Au <==> @Au("你的默认权限") 默认权限的名字可设置

  2. 如果@Au加在Controller上面,则里面的所有路径都会带上controller上面的权限

  3. exclude优先级很高,在你加上exclude = "zxc"时,就算你有也会被排除在外

@AuUser注解

  1. 在你没有登录时,如果没有加上authority = false,那么将获得不到User。但是你任可以访问这个路径

  2. 如果你登录了。那么Au会通过你所提交在header里的的jwt来解析,如果解析成功,那么会调用你的getUser方法来获得User,这一步相当于3步

    1. 第一步 String token = request.getHeader("authorization"); 获得头里面token
    2. 第二步 Claims claims = jwtHelper.parseToken(token); 解析token
    3. 第三部 claims.get("userId") 然后走getUser,来获得User。

注意的是,他不会走login。

  1. 在加了@AuUser注解的地方,这个方法的其他所有路径,都将加上默认角色的权限。即test2方法相当于test1方法,只不过多了一个从header中取user的行为。
  2. 如上图Au会更具上面的角色,自动拦截没有权限的用户,权限认证利用的是Jwt认证。

6. 登录、注册。

注册同登录,道理是一样的,这里讲登录

@RestController
@RequestMapping("/user")
public class AuLoginTestController {

    @Autowired
    private AuHelper auHelper;

  
    @PostMapping("/login")
    public Result login(@RequestBody User loginUser) {
      
        AuHelper.AuRet ret = auHelper.login(null, loginUser);
      
        if (ret.loginStatus()) {
            return new Result("success", ret);
        } else {
            return new Result("login error", ret);
        }
      
    }
  
  

}
  • 如上,这个会走你登录流程。当然这是没加密的,而且,这个登录只是post请求,没有get请求。

现在加上@Decrypt解密注解,然后来看下面这种

@RestController
@RequestMapping("/user")
public class AuLoginTestController {

    @Autowired
    private AuHelper auHelper;

  
    @RequestMapping(value = "/login", method = {RequestMethod.POST, RequestMethod.GET})
    public Result login(@AuUser(authority = false) User user,
                        @RequestBody(required = false) @Decrypt User requestUser) {
      
        AuHelper.AuRet ret = auHelper.login(user, requestUser);
      
        if (ret.loginStatus()) {
            return new Result(ResultCode.SUCCESS, ret);
        } else {
            return new Result(ResultCode.LOGIN_ERROR);
        }
      
        }
}
  有几个问题
  1. User如何解密。前端如何加密。
  2. Get方法用来干嘛,为什么登录要2个User
  3. 如果jwt解析错误,会怎么办
1、第一个问题:User如何解密。前端如何加密。
先说加密,Au是基于RSA公私钥模式,所以你得先请求后端,获得公钥匙,然后用公钥加密

后端需要给一个获取公钥的借口,如下。

@RestController
@RequestMapping("/au")
@Slf4j
public class AuController {

    @Autowired
    private AuHelper auHelper;

    @GetMapping("")
    public Result getPublicKy() {
        return new Result(ResultCode.SUCCESS, auHelper.getPublicKey());
    }

}

然后在前端先请求公钥的接口,然后用公钥加密,把秘文放在对象中,在传递给后端,注意得放在对象中,否则会解析异常,参数格式异常。

let user = {
    username: "zhouxinchen",
    password: "123456"
} //这是你的user


axios.post("/user/login", user).then(res => {
       ...
      // 这样肯定不行
})


// 使用前端常见的加密工具类 jsencrypt
<script src="https://cdn.bootcdn.net/ajax/libs/jsencrypt/3.2.1/jsencrypt.min.js"></script>
  
request.get("/au").then(res => {
            let publicKey = res.data.data; // 获得公钥匙
        let encryptor = new JSEncrypt()  // 创建加密对象实例
        encryptor.setPublicKey(publicKey)//设置公钥
  
          let content = encryptor.encrypt(JSON.stringify(user)); // 先转json,再给整个对象加密
  
            request.post("/user/login",{ content }).then(res => {
            console.log(res.data); // 注意是 { content }
        })
  
})

再说解密,不用关心解密流程,只要加上@Decrypt   Au会自己用自己的私钥来解密。然后再返给springboot,让他去解析到对象中。
2. Get方法用来干嘛,为什么登录要2个User

作用:

第一个User就是jwt里面的user。

前端将登录之后返回给的jwt存在浏览器中,下次再次进入登录界面时,会先get请求登录接口,且如果已经有jwt,且解析正确(jwt正常、没过期、密钥没换),那么会直接给你User,(这时候调用的是你的getUser方法),这样子就不用一直登录了。看起来就像和服务器一直保持连接一样。换句话说就是remenber me。

3. 如果jwt解析错误,会怎么办

只有在标记@AuUser的情况下才会解析错误,不论有没有authority = false/true,这时候的user都是null,如果你是get请求,请直接返回客户,让他重新登录。如果是post请求。则AuHelper.AuRet ret = auHelper.login(user, requestUser); ==》AuHelper.AuRet ret = auHelper.login(null, requestUser); 其实没什么影响

@ControllerAdvice
@ResponseBody
@Slf4j
public class WebExceptionHandler {

  
    @ExceptionHandler(MalformedJwtException.class)
    public Result malformedJwtException(MalformedJwtException e) {
        log.error("jwt格式异常", e);
        return new Result(ResultCode.JWT_MALFORMED_ERROR);
    }
  
    @ExceptionHandler(ExpiredJwtException.class)
    public Result expiredJwtException(ExpiredJwtException e) {
        log.error("jwt过期", e);
        return new Result(ResultCode.LOGIN_EXPIRE);
    }

    @ExceptionHandler(Exception.class)
    public Result unknownException(Exception e) {
        log.error("发生了未知异常", e);
        return new Result(ResultCode.SERVER_ERROR);
    }
  
}

7. RSA加密解密

在需要解密的地方加上@Decrypt注解,既可以实现解密

注意。这里可以加在参数上也可以加在方法上,如果加在参数上,那么所有的参数都会解密,如果加在参数上,只有那个参数才会解密。如果此时是post方法,有@RequestBody,那么需要前端进行包装,如下。然后传给后端,后端再来加密。

前端加密通过请求后端接口,然后用公钥加密。

🌟🌟🌟🌟🌟🌟🌟点个star吧~🌟🌟🌟🌟🌟🌟🌟

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容