Java注釈による権限管理


簡単な権限制御シーンで、ログインユーザ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 {
         
    
    }