Delegating Authentication to Microservices with Custom Spring Annotations

This article explains how to move authentication from the gateway to individual Spring Cloud microservices by defining three custom annotations and an AOP aspect, while also covering Feign‑based calls and providing practical code examples for implementation.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Delegating Authentication to Microservices with Custom Spring Annotations

Implementation Idea

Previous articles placed authentication and authorization together at the gateway using ReactiveAuthorizationManager . The new approach delegates authentication to downstream microservices, letting the gateway only perform routing.

To achieve this, modify the gateway configuration to permit all requests:

http
 ....
 // whitelist directly permitted
 .pathMatchers(ArrayUtil.toArray(whiteUrls.getUrls(), String.class)).permitAll()
 // all other requests also permitted
 .anyExchange().permitAll()
 .....

1. Remove the Authorization Manager

The gateway no longer uses the ReactiveAuthorizationManager ; all requests bypass it.

2. Define Three Custom Annotations

Spring Security already provides @Secured, @PreAuthorize, and @PostAuthorize, but this article creates its own: @RequiresLogin – only logged‑in users can access the annotated method. @RequiresPermissions – only users with specified permission codes can access. @RequiresRoles – only users with specified roles can access.

Annotation definitions:

/**
 * @author 公众号:码猿技术专栏
 * @url www.java-family.cn
 * @description 登录认证的注解,标注在controller方法上,一定要是登录才能的访问的接口
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresLogin {}
/**
 * @author 公众号:码猿技术专栏
 * @url www.java-family.cn
 * @description 标注在controller方法上,确保拥有指定权限才能访问该接口
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresPermissions {
    String[] value() default {};
    Logical logical() default Logical.AND;
}
/**
 * @author 公众号:码猿技术专栏
 * @url www.java-family.cn
 * @description 标注在controller方法上,确保拥有指定的角色才能访问该接口
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresRoles {
    String[] value() default {OAuthConstant.ROLE_ROOT_CODE, OAuthConstant.ROLE_ADMIN_CODE};
    Logical logical() default Logical.AND;
}

3. Annotation Aspect Definition

The aspect intercepts methods annotated with any of the three custom annotations and performs the corresponding checks:

@Aspect
@Component
public class PreAuthorizeAspect {
    public static final String POINTCUT_SIGN = " @annotation(com.mugu.blog.common.annotation.RequiresLogin) || @annotation(com.mugu.blog.common.annotation.RequiresPermissions) || @annotation(com.mugu.blog.common.annotation.RequiresRoles)";

    @Pointcut(POINTCUT_SIGN)
    public void pointcut() {}

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        checkMethodAnnotation(signature.getMethod());
        try {
            return joinPoint.proceed();
        } catch (Throwable e) {
            throw e;
        }
    }

    public void checkMethodAnnotation(Method method) {
        RequiresLogin loginAnno = method.getAnnotation(RequiresLogin.class);
        if (loginAnno != null) { doCheckLogin(); }

        RequiresRoles rolesAnno = method.getAnnotation(RequiresRoles.class);
        if (rolesAnno != null) { doCheckRole(rolesAnno); }

        RequiresPermissions permAnno = method.getAnnotation(RequiresPermissions.class);
        if (permAnno != null) { doCheckPermissions(permAnno); }
    }

    private void doCheckLogin() {
        LoginVal loginVal = SecurityContextHolder.get();
        if (Objects.isNull(loginVal)) {
            throw new ServiceException(ResultCode.INVALID_TOKEN.getCode(), ResultCode.INVALID_TOKEN.getMsg());
        }
    }

    private void doCheckRole(RequiresRoles requiresRoles) {
        String[] roles = requiresRoles.value();
        LoginVal loginVal = OauthUtils.getCurrentUser();
        String[] authorities = loginVal.getAuthorities();
        boolean match = false;
        if (requiresRoles.logical() == Logical.AND) {
            match = Arrays.stream(authorities).filter(StrUtil::isNotBlank).allMatch(item -> CollectionUtil.contains(Arrays.asList(roles), item));
        } else {
            match = Arrays.stream(authorities).filter(StrUtil::isNotBlank).anyMatch(item -> CollectionUtil.contains(Arrays.asList(roles), item));
        }
        if (!match) {
            throw new ServiceException(ResultCode.NO_PERMISSION.getCode(), ResultCode.NO_PERMISSION.getMsg());
        }
    }

    private void doCheckPermissions(RequiresPermissions requiresPermissions) {
        // TODO: implement permission check based on business needs
    }
}
“ @RequiresPermissions logic is not implemented here; you need to complete it according to your own business requirements.”

4. Using the Annotations

Example: only users with admin or root roles can add an article:

@RequiresRoles
@AvoidRepeatableCommit
@ApiOperation("Add Article")
@PostMapping("/add")
public ResultMsg<Void> add(@RequestBody @Valid ArticleAddReq req) {
    // ...
}

When a Feign client calls a downstream service that is protected by @RequiresRoles, the call will be rejected for users lacking the required role, returning “No permission”.

Conclusion

The article demonstrates how to shift authentication from the gateway to individual microservices using custom annotations and an AOP aspect. In production, keep authentication centralized at the gateway unless specific business scenarios require decentralization.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMicroservicesaopAuthenticationSpring CloudAuthorizationCustom Annotations
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.