栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

自定义权限校验

Java 更新时间:发布时间: 百科书网 趣学号

最近写一个小应用,有涉及到权限控制的,不想引入SpringSecurity等权限框架,感觉用框架太重了,于是自己用拦截器简单实现了下。思路就是自定义一个注解,标注在需要权限控制Contoller的方法上,该注解有一个roles属性,表示接口需要的角色,定义一个拦截器,拦截每个请求,根据请求头携带的Token查询用户信息,判断用户角色中是否有注解声明的某个角色,如果有权限就放行,没有就拦截抛异常。

数据库表结构

登录

因为会拦截所有请求,从请求头中获取TokenID查询用户信息,而这个TokenID就是登录成功时返回给前端,由前端保存,在后续发起请求时,放在请求头中传给后端。在当前这个应用中,是小程序登录,根据前端传的code加上appid、appsecret,请求微信登录接口返回openId,确认是系统用户,就生成一个tokenId存在数据库中,并返回给前端。

自定义注解

使用注解可以很方便用来标识接口是否需要权限,需要哪些权限,类似于SpringSecurity的@PreAuthorize注解。自定义注解代码如下

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HasAnyRole {

    Role[] value();
}

public enum Role {
    ADMIN, NORMAL
}

注解只能标注在方法上,并且有一个属性用来表示接口需要的权限,Role是一个枚举,表示系统中所有的角色。

拦截器
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private Isolator isolator;

    @Autowired
    private TokenMapper tokenMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{

        log.info("this is login interceptor");
        if(handler instanceof HandlerMethod){
            HasAnyRole needAuthority = ((HandlerMethod) handler).getMethodAnnotation(HasAnyRole.class);
            if (needAuthority == null || needAuthority.value().length == 0) {
                return true;
            }
            String authorization = request.getHeader("Authorization");
            if (StringUtils.isEmpty(authorization) || !authorization.startsWith("Token ")) {
                log.info("current user not login");
                response.sendError(401, "请先登录");
                return false;
            }

            String tokenId = authorization.split(" ")[1];
            List roles = isolator.getRolesByTokenId(tokenId);
            if(CollectionUtils.isEmpty(roles)) {
                log.info("token is expire or user has empty roles");
                response.sendError(401, "Token过期或用户没有角色");
                return false;
            }

            boolean res = Arrays.stream(needAuthority.value()).anyMatch(roles::contains);
            if (res) {
                tokenMapper.extend(tokenId);
                return true;
            }
            log.info("current user has no role to visit current interface");
            response.sendError(403, "暂无权限访问");
            return false;

        }
        log.info("handler type is not HandlerMethod");
        return false;
    }
}

实现HandlerInterceptor接口,重写preHandle方法,Isolator属性是用来根据tokenID获取该用户的所有权限的,并且是带Mock的功能,在跑测试的时候是直接获取所有角色。tokenMapper就是操作token表的,逻辑就是和文章开头说的一样,从request对象拿tokenId,再获取用户的角色,获取方法标注的自定义注解,拿到里面声明的角色列表,判断用户拥有的角色是否有一个在注解声明的角色列表中,如果有就通过校验,否则返回403。如果接口需要权限但是请求头中没有带tokenId说明没有登录,返回401。

拦截器还需要配置,设置拦截规则。

@Configuration
public class WebConfigurer implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(loginInterceptor)
            .addPathPatterns("/**");
    }
}
使用

在需要角色校验的地方加上自定义注解即可

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

    @Autowired
    private IUserService iUserService;

    @PostMapping("/login")
    public UserLoginVO login(@RequestBody UserLoginCommand command) {
        String tokenId = iUserService.login(command.getCode());
        return UserLoginVO.from(tokenId);
    }

    @GetMapping("/test")
    @HasAnyRole({Role.NORMAL, Role.ADMIN})
    public String test() {
        return "111";
    }
}

一般小应用可以就使用简单的方式,如果稍微复杂点的应用,权限控制可以抽出来,放到网关中进行处理,可以再配合Redis和Jwt进行优化。

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/888714.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号