java 基于Security自定义注解跳过权限判断

  • 需求:客户端接口有的需要权限控制,有的不需要权限控制
  • 技术栈:Spring Security

步骤一:自定义注解

@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Import(AuthControlRegistrar.class)
@Component
public @interface ClientAuthControl {

    String value() default "";

}

步骤二:扫描指定注解

@Slf4j
@Component
public class AuthControlRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    private static Set<BeanDefinition> candidateComponents = new HashSet<>();

    public static Set<String> URL_NOT_AUTH = new HashSet<>();

    @Override
    public void setEnvironment(Environment environment) {
          this.environment = environment;
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {

        // 筛选指定注解
        ClassPathScanningCandidateComponentProvider scan = new ClassPathScanningCandidateComponentProvider(false, this.environment);

        scan.addIncludeFilter(new AnnotationTypeFilter(ClientAuthControl.class));

        String packageName = ClassUtils.getPackageName(annotationMetadata.getClassName());

        candidateComponents.addAll(scan.findCandidateComponents(packageName));
    }


    @SneakyThrows
    @PostConstruct
    private void getNotAuthInfo() {

        // 根据筛选的注解进行 权限控制
        for (BeanDefinition candidateComponent : candidateComponents) {

            // 反射获取注解类的方法属性
            String beanClassName = candidateComponent.getBeanClassName();
            Class<?> aClass = Class.forName(beanClassName);
            RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);

            String classUrl = "";
            if (annotation != null) {
                String[] classUrls = annotation.value();
                classUrl = arrToString(classUrls);
            }


            Method[] methods = aClass.getDeclaredMethods();

            for (Method method : methods) {

                if (!method.isAnnotationPresent(ClientAuthControl.class)){
                    continue;
                }

                String mode;
                String url ;

                RequestMapping requestMapping;
                GetMapping getMapping;
                PostMapping postMapping;
                PutMapping putMapping;
                DeleteMapping deleteMapping;
                PatchMapping patchMapping;

                if (null != (requestMapping = method.getAnnotation(RequestMapping.class))){
                    String[] value = requestMapping.value();
                    RequestMethod[] requestMethods = requestMapping.method();
                    mode = arrToString(requestMethods);
                    url = arrToString(value);
                }else if (null != (getMapping = method.getAnnotation(GetMapping.class))) {
                    String[] value = getMapping.value();
                    mode = RequestMethod.GET.name();
                    url = arrToString(value);
                }else if (null != (postMapping = method.getAnnotation(PostMapping.class))) {
                    String[] value = postMapping.value();
                    mode = RequestMethod.POST.name();
                    url = arrToString(value);
                }else if (null != (putMapping = method.getAnnotation(PutMapping.class))) {
                    String[] value = putMapping.value();
                    mode = RequestMethod.PUT.name();
                    url = arrToString(value);
                }else if (null != (deleteMapping = method.getAnnotation(DeleteMapping.class))) {
                    String[] value = deleteMapping.value();
                    mode = RequestMethod.DELETE.name();
                    url = arrToString(value);
                }else if (null != (patchMapping = method.getAnnotation(PatchMapping.class))) {
                    String[] value = patchMapping.value();
                    mode = RequestMethod.PATCH.name();
                    url = arrToString(value);
                }else continue;


                // 判断是否以"/"开头 不是则加上
                if (!url.startsWith("/")) {
                    url = "/" + url;
                }


                // 判断URL上是否有动态参数/{}
                if (url.contains("{") && url.contains("}")) {
                    url = url.substring(0,url.indexOf("{"));
                }


                url = classUrl + url;

                URL_NOT_AUTH.add(String.format("%s_%s",mode,url));
            }
        }
    }


    private String arrToString(Object[] arr) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < arr.length; i++) {
            sb.append(arr[i].toString());
            if (i != (arr.length -1)) {
                sb.append(",");
            }
        }

        return sb.toString();
    }

步骤三:权限判断

@Slf4j
@Component
public class TokenOncePerRequestFilter extends OncePerRequestFilter {

    @Resource
    private JwtProperties jwtProperties;

    @Resource
    JwtUser jwtUser;

    @Resource
    private MyUserDetailsService myUserDetailsService;

    /**
     *
     * @param request
     * @return
     */
    private boolean notRequiresAuthentication(HttpServletRequest request) {
        String uri = request.getRequestURI();

        for (String s : START_WITH_PERMIT_ALL_URL){
            if (uri.startsWith(s)) {
                return true;
            }
        }


        return false;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        BaseRes<String> res = new BaseRes<>(TOKEN_INVALID);

        out: if (!this.notRequiresAuthentication(request)) {

            // 获取请求的token
            String authToken = request.getHeader(jwtProperties.getHeader());
            if (StringUtils.isBlank(authToken)) {
                authToken = request.getParameter("access_token");
            }

            String tokenHead = "Bearer ";
            String uri = request.getRequestURI();


            // 判断接口是否需要权限
            String method = request.getMethod();
            String urlFlag = String.format("%s_%s",method,uri);

            for (String authUrl : AuthControlRegistrar.URL_NOT_AUTH) {
                if (authUrl.contains(urlFlag) || urlFlag.contains(authUrl)) {
                    break out;
                }
            }

            if (StringUtils.isBlank(authToken) || !authToken.startsWith(tokenHead)) {

                log.error("token is null uri={}, url={}", request.getRequestURI(), request.getRequestURL());
                CommUtils.printObjJson(response, res);
                return;
            }


            authToken = authToken.substring(tokenHead.length());

            try {
                // 验证token是否有效
                String rawJson = jwtUser.getClaimByToken(authToken);

                /**
                 * 登录用户信息设置
                 */
                TokenUser tokenUser = myUserDetailsService.loadUserByUsername(rawJson);

                String type = tokenUser.getType();

                String s = String.format("/%s", type).toLowerCase();

                // 判断用户类型 是否请求对应的接口
                if (!uri.startsWith(s)) {
                    CommUtils.printObjJson(response, res);
                    return;
                }


                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(tokenUser, null, tokenUser.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);

            } catch (Exception e) { // JWTDecodeException SignatureVerificationException
                ExceptionUtils.setExceptionMsg(e, res);
                CommUtils.printObjJson(response, res);
                return;
            }
        }

        filterChain.doFilter(request, response);
    }
}

步骤四:因为只做了后台动态权限,客户端则需要把所有权限添加进去


    public static final String CLIENT_START_URL = "/client";

 	@Override
    protected void configure(HttpSecurity http) throws Exception {

        http.headers().frameOptions().disable();
        http.csrf().disable();

        initAuth(http); // 初始化权限
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 无session状态

        http
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers(CLIENT_START_URL + "/**").permitAll()
                .anyRequest().authenticated()
        ;

        http.addFilterAfter(tokenOncePerRequestFilter, SecurityContextPersistenceFilter.class);

        // handling
        http.exceptionHandling()
                .authenticationEntryPoint(entryPointUnauthorizedHandler)// 身份认证
                .accessDeniedHandler(restAccessDeniedHandler) // 授权
        ;

    }

步骤五:添加注解到Controller 的类上和指定方法上

@RestController
@RequestMapping("/client/policy")
@ClientAuthControl
public class PartyPolicyClientController {
    @Resource
    private PartyPolicyService partyPolicyService;


    @PostMapping("/list")
    @ClientAuthControl
    public BaseRes<IPage<PartyPolicyVo>> selectPartyPolicyList(@RequestBody PageMessage<PartyPolicyVo> pageMessage) {
        return partyPolicyService.selectPartyPolicyList(pageMessage.getT(),pageMessage.getPage());
    }

    @GetMapping("/info")
    @ClientAuthControl
    public BaseRes<PartyPolicy> getPartyPolicyDetail(@RequestParam("id") Integer id) {
        return partyPolicyService.getPartyPolicyDetail(id);
    }

}

大功告成。客户端请求该接口时则跳过权限判断

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿小张丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值