# Au **Repository Path**: krelvis/au ## Basic Information - **Project Name**: Au - **Description**: Au权限框架 基于Springboot的权限认证框架,整合了RSA、JWT。使用了RBAC模式。省去了很多繁琐的配置、使用非常简单。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-29 - **Last Updated**: 2021-08-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # **Au权限框架** 基于Springboot的权限认证框架,整合了RSA、JWT。使用了RBAC模式。省去了很多繁琐的配置、使用非常简单。 > 基于springboot 集成了jwt的权限认证以及RBAC(Role-Based Access Control)多角色控制资源,以及集成了RSA的自动验证参数的功能、同时用quartz自动化来自动刷新的公私秘钥 - demo地址 https://gitee.com/iozxc/au-demo 先看yml配置。 ```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包 ```xml cn.omisheep.au au-spring-boot-start 1.1.1 ``` ### 2. 开启Au 用@AuEnable标记主接口 ```java @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** ```java @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。 ```java @Component public class UserData implements AuUserData { @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注解即可 ```java @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。 > >3. 在加了@AuUser注解的地方,这个方法的其他所有路径,都将加上默认角色的权限。即test2方法相当于test1方法,只不过多了一个从header中取user的行为。 >4. 如上图Au会更具上面的角色,自动拦截没有权限的用户,权限认证利用的是Jwt认证。 ### 6. 登录、注册。 注册同登录,道理是一样的,这里讲登录 ```java @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`解密注解,然后来看下面这种 ```java @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公私钥模式,所以你得先请求后端,获得公钥匙,然后用公钥加密 后端需要给一个获取公钥的借口,如下。 ```java @RestController @RequestMapping("/au") @Slf4j public class AuController { @Autowired private AuHelper auHelper; @GetMapping("") public Result getPublicKy() { return new Result(ResultCode.SUCCESS, auHelper.getPublicKey()); } } ``` 然后在前端先请求公钥的接口,然后用公钥加密,把秘文`放在对象中`,在传递给后端,注意得放在对象中,否则会解析异常,参数格式异常。 ```javascript let user = { username: "zhouxinchen", password: "123456" } //这是你的user axios.post("/user/login", user).then(res => { ... // 这样肯定不行 }) // 使用前端常见的加密工具类 jsencrypt 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); 其实没什么影响 > ```java @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吧~🌟🌟🌟🌟🌟🌟🌟