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注解
@Au <==> @Au("你的默认权限") 默认权限的名字可设置
如果@Au加在Controller上面,则里面的所有路径都会带上controller上面的权限
exclude优先级很高,在你加上
exclude = "zxc"时,就算你有也会被排除在外
@AuUser注解
在你没有登录时,如果没有加上authority = false,那么将获得不到User。但是你任可以访问这个路径
如果你登录了。那么Au会通过你所提交在header里的的jwt来解析,如果解析成功,那么会调用你的getUser方法来获得User,
这一步相当于3步
- 第一步 String token = request.getHeader("authorization"); 获得头里面token
- 第二步 Claims claims = jwtHelper.parseToken(token); 解析token
- 第三部 claims.get("userId") 然后走getUser,来获得User。
注意的是,他不会走login。
- 在加了@AuUser注解的地方,这个方法的其他所有路径,都将加上默认角色的权限。即test2方法相当于test1方法,只不过多了一个从header中取user的行为。
- 如上图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);
}
}
}
有几个问题
- User如何解密。前端如何加密。
- Get方法用来干嘛,为什么登录要2个User
- 如果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吧~🌟🌟🌟🌟🌟🌟🌟