自定义权限管理:用户即角色
2023-12-22 09:35:35
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
还是一样,只贴出核心代码。
SQL:t_user增加user_type
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`user_type` tinyint(1) NOT NULL DEFAULT '4' COMMENT '用户类型 1-管理员 2-教师 3-学生 4-游客',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of t_user
-- ----------------------------
BEGIN;
INSERT INTO `t_user` VALUES (1, 'bravo1988', '123456', 1, '2021-02-13 20:01:44', '2021-02-15 10:29:46', 0);
INSERT INTO `t_user` VALUES (2, 'bravo', '123456', 4, '2021-02-14 10:37:25', '2021-02-14 10:37:25', 0);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
权限注解
/**
* 登录权限注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface LoginRequired {
}
/**
* 角色权限注解
* 注意:由于默认权限都是针对用户而言,所以我在PermissionRequired上加了LoginRequired
*/
@LoginRequired
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface PermissionRequired {
/**
* 角色,默认游客权限
*
* @return
*/
UserType[] userType() default {UserType.VISITOR};
/**
* 逻辑关系,比如 ADMIN&&TEACHER 或者 ADMIN||TEACHER
*
* @return
*/
Logical logical();
}
枚举
/**
* 当前设计不支持一个用户有多个角色,所以AND暂时用不到,而OR的作用更多的是提示当前逻辑是“符合其中一个角色即可”
*/
@Getter
public enum Logical {
AND,
OR;
}
/**
* 用户角色枚举
*/
@Getter
public enum UserType {
ADMIN(1, "管理员"),
TEACHER(2, "教师"),
STUDENT(3, "学生"),
VISITOR(4, "游客");
private final Integer value;
private final String desc;
UserType(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
/**
* 是否有权限
*
* @param userTypes
* @param typeOfUser
* @param logical
* @return
*/
public static boolean hasPermission(UserType[] userTypes, Integer typeOfUser, Logical logical) {
Objects.requireNonNull(userTypes);
Objects.requireNonNull(logical);
// 1.1 要求单个角色权限,且当前用户刚好是这个角色
if (userTypes.length == 1 && userTypes[0].getValue().equals(typeOfUser)) {
return true;
}
// 1.2 要求多个角色权限
if (userTypes.length > 1) {
// AND:预留,当前设计其实不支持一个用户有多个角色
if (Logical.AND.equals(logical)) {
return false;
}
// OR:只要用户拥有其中一个角色即可
if (Logical.OR.equals(logical)) {
for (UserType type : userTypes) {
if (type.getValue().equals(typeOfUser)) {
return true;
}
}
}
}
return false;
}
}
权限拦截(关键代码)
public class SecurityInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 不拦截跨域请求相关
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
// 如果方法上没有加@LoginRequired或@PermissionRequired(上面叠加了@LoginRequired),直接放行
if (isLoginFree(handler)) {
return true;
}
// 登录校验
User user = handleLogin(request, response);
ThreadLocalUtil.put(WebConstant.USER_INFO, user);
// 权限校验
checkPermission(user, handler);
// 放行到Controller
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 及时移除,避免ThreadLocal内存泄漏
ThreadLocalUtil.remove(WebConstant.USER_INFO);
super.afterCompletion(request, response, handler, ex);
}
/**
* 接口是否免登录(支持Controller上添加@LoginRequired)
*
* @param handler
* @return
*/
private boolean isLoginFree(Object handler) {
// 判断是否支持免登录
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 类上是否有@LoginRequired
Class<?> controllerClazz = handlerMethod.getBeanType();
LoginRequired ControllerLogin = AnnotationUtils.findAnnotation(controllerClazz, LoginRequired.class);
// 方法上是否有@LoginRequired
Method method = handlerMethod.getMethod();
LoginRequired methodLogin = AnnotationUtils.getAnnotation(method, LoginRequired.class);
return ControllerLogin == null && methodLogin == null;
}
return true;
}
/**
* 是否需要权限认证(支持Controller上添加@PermissionRequired)
*
* @param handler
* @return
*/
private boolean isPermissionFree(Object handler) {
// 判断是否需要权限认证
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Class<?> controllerClazz = handlerMethod.getBeanType();
Method method = handlerMethod.getMethod();
PermissionRequired controllerPermission = AnnotationUtils.getAnnotation(controllerClazz, PermissionRequired.class);
PermissionRequired methodPermission = AnnotationUtils.getAnnotation(method, PermissionRequired.class);
// 没有加@PermissionRequired,不需要权限认证
return controllerPermission == null && methodPermission == null;
}
return true;
}
/**
* 登录校验
*
* @param request
* @param response
* @return
*/
private User handleLogin(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
User currentUser = (User) session.getAttribute(WebConstant.CURRENT_USER_IN_SESSION);
if (currentUser == null) {
// 抛异常,请先登录
throw new BizException(ExceptionCodeEnum.NEED_LOGIN);
}
return currentUser;
}
/**
* 权限校验
*
* @param user
* @param handler
*/
private void checkPermission(User user, Object handler) {
// 如果方法上没有加@PermissionRequired,直接放行
if (isPermissionFree(handler)) {
return;
}
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
Class<?> controllerClazz = handlerMethod.getBeanType();
PermissionRequired controllerPermission = AnnotationUtils.findAnnotation(controllerClazz, PermissionRequired.class);
PermissionRequired methodPermission = AnnotationUtils.getAnnotation(method, PermissionRequired.class);
if (hasPermission(controllerPermission, user.getUserType()) && hasPermission(methodPermission, user.getUserType())) {
return;
}
// 代码走到这,说明权限不匹配
throw new BizException(ExceptionCodeEnum.PERMISSION_DENY);
}
}
private boolean hasPermission(Annotation permissionAnnotation, Integer typeOfUser) {
if (permissionAnnotation == null) {
return true;
}
UserType[] userTypes = (UserType[]) AnnotationUtils.getValue(permissionAnnotation, "userType");
Logical logical = (Logical) AnnotationUtils.getValue(permissionAnnotation, "logical");
// 我把权限判断的逻辑封装到UserType枚举类中复用
return UserType.hasPermission(userTypes, typeOfUser, logical);
}
}
测试
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private HttpSession session;
@PostMapping("/register")
public Result<User> register(@RequestBody User userInfo) {
int rows = userMapper.insert(userInfo);
if (rows > 0) {
return Result.success(userInfo);
}
return Result.error("插入失败");
}
@PostMapping("/login")
public Result<User> login(@RequestBody User loginInfo) {
LambdaQueryWrapper<User> lambdaQuery = Wrappers.lambdaQuery();
lambdaQuery.eq(User::getName, loginInfo.getName());
lambdaQuery.eq(User::getPassword, loginInfo.getPassword());
User user = userMapper.selectOne(lambdaQuery);
if (user == null) {
return Result.error("用户名或密码错误");
}
session.setAttribute(WebConstant.CURRENT_USER_IN_SESSION, user);
return Result.success(user);
}
@LoginRequired
@GetMapping("/needLogin")
public Result<String> needLogin() {
return Result.success("if you see this, you are logged in.");
}
@GetMapping("/needNotLogin")
public Result<String> needNotLogin() {
return Result.success("if you see this, you are logged in.");
}
@PermissionRequired(userType = {UserType.ADMIN,UserType.TEACHER}, logical = Logical.OR)
@GetMapping("/needPermission")
public Result<String> needPermission() {
return Result.success("if you see this, you has the permission.");
}
}
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
进群,大家一起学习,一起进步,一起对抗互联网寒冬
文章来源:https://blog.csdn.net/smart_an/article/details/135144269
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!