一个简单的权限控制场景,已知登录用户id,判断这个用户是否存在数据库中,如果不存在则不允许进行任何操作。
关于java注解介绍请参见 Java自定义注解实现权限管理
在每个controller方法中添加用户校验代码,这种可以控制到方法级,但是每个方法都要维护这段重复逻辑。
@RequestMapping(value = "/task/progress", method = RequestMethod.GET)
public RestRsp getLabelTaskProgress(
@RequestParam(name = "taskId", defaultValue = "-1") long taskId,
@Visitor long userId
) {
// 校验用户
if (!labelService.checkUserValid(userId)) {
return RestRsp.success(new ListRsp());
}
// 获取标注进度
... 省略
return RestRsp.success(listRsp);
}
spring的拦截器实现,好处是增加在进入controller方法前提前拦截非法用户,但是设计到不同controller类或方法不同权限时,只能通过api路径区分,不方便。
@Slf4j
public class AuthInterceptor extends HandlerInterceptorAdapter {
private final static String LABEL_PATH_PATTERN = "^/*webapi/+label/*.*$";
private static final String ERROR_MSG = "您没有权限,请联系管理";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
if (debugHost() && hasDebugParam(request)) {
return true;
}
String uri = request.getRequestURI();
log.info("uri:{}", uri);
String username = SsoUserInfo.getUserName();
if (Pattern.matches(LABEL_PATH_PATTERN, uri)) {
Set<String> managerSet = LABEL_MANAGER_SET.get();
if (!managerSet.contains(username)) {
throw new ServiceException(ErrorCode.PERMISSION_DENIED);
}
} else {
Set<String> userSet = USER_SET.get();
if (!userSet.contains(username)) {
throw ServiceException.ofMessage(ErrorCode.PERMISSION_DENIED, ERROR_MSG);
}
}
return true;
}
}
注解+拦截器的实现方式,既可以实现不同粒度的权限控制,也可以集中管理权限。
public enum AuthEnum {
USER_LABEL("label_user", "标注用户", "非标注用户禁止使用"),
;
private String code;
private String desc;
private String info;
AuthEnum(String code, String desc, String info) {
this.code = code;
this.desc = desc;
this.info = info;
}
public String getCode() {
return this.code;
}
public String getDesc() {
return this.desc;
}
public String getInfo() {
return this.info;
}
}
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD, ElementType.TYPE})
public @interface AuthAnn {
AuthEnum[] authType();
}
@Slf4j
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
@Autowired
private LabelService labelService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
Long userId = SSOHelper.getUserId(request);
AuthAnn authAnn = ((HandlerMethod) handler).getMethodAnnotation(AuthAnn.class);
if (authAnn == null || ObjectUtils.isEmpty(authAnn.authType())) {
authAnn = ((HandlerMethod) handler).getBeanType().getAnnotation(AuthAnn.class);
}
if (authAnn == null || ObjectUtils.isEmpty(authAnn.authType())) {
return true;
}
//log.info("userId:{}", userId);
AuthEnum[] authEnums = authAnn.authType();
for (AuthEnum authEnum : authEnums) {
if (AuthEnum.USER_LABEL.equals(authEnum)) {
if (!labelService.checkUserExist(userId)) {
log.info("Invalid Label User, userId:{}", userId);
throw ServiceException.ofMessage(ErrorCode.PERMISSION_DENIED, authEnum.getInfo());
}
}
}
return true;
}
}
@Configuration
@Slf4j
public class ProphetInterceptorConfiguration implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
String[] paths = passportProperties.urlPaths();
registry.addInterceptor(AuthInterceptor).addPathPatterns(paths);
}
}
@Slf4j
@RestController
@RequestMapping("/api/label")
@AuthAnn(authType = AuthEnum.USER_LABEL)
public class LabelController {
}