# 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吧~🌟🌟🌟🌟🌟🌟🌟