Backend Development 17 min read

Master Microservice Authentication with Sa-Token and Spring Cloud Gateway

This tutorial walks through building a microservice authentication and authorization solution using Sa-Token, Spring Cloud Gateway, Nacos, and Redis, detailing the setup of gateway, authentication, and protected API services, configuration files, custom permission handling, and a complete demo with Postman.

macrozheng
macrozheng
macrozheng
Master Microservice Authentication with Sa-Token and Spring Cloud Gateway

Prerequisite Knowledge

We will use Nacos as the service registry, Spring Cloud Gateway as the API gateway, and Sa-Token for microservice permission management. The following articles provide background information.

Spring Cloud Gateway: Next‑gen API gateway service

Spring Cloud Alibaba: Using Nacos as registry and config center

Microservice Permission Solution with Spring Cloud Gateway + OAuth2

Sa-Token Tutorial

Application Architecture

The architecture follows the previous solution: the authentication service handles login, the gateway performs authentication and permission checks, and each API service contains its own business logic. All services share Sa-Token sessions via Redis.

micro-sa-token-common: shared DTOs such as

UserDTO

and

CommonResult

micro-sa-token-gateway: gateway service for request forwarding, login authentication, and permission verification

micro-sa-token-auth: authentication service exposing a login endpoint

micro-sa-token-api: protected API service accessed after gateway authentication

Solution Implementation

We will sequentially build the gateway, authentication, and API services.

micro-sa-token-gateway

First, set up the gateway service, which will handle authentication and authorization for all microservices.

Add the following dependencies to

pom.xml

:

<code>&lt;dependencies&gt;
    &lt;!-- Sa-Token permission (reactive) --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;cn.dev33&lt;/groupId&gt;
        &lt;artifactId&gt;sa-token-reactor-spring-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;1.24.0&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- Sa-Token Redis integration (Jackson) --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;cn.dev33&lt;/groupId&gt;
        &lt;artifactId&gt;sa-token-dao-redis-jackson&lt;/artifactId&gt;
        &lt;version&gt;1.24.0&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- Redis connection pool --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
        &lt;artifactId&gt;commons-pool2&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;!-- Common module --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.macro.cloud&lt;/groupId&gt;
        &lt;artifactId&gt;micro-sa-token-common&lt;/artifactId&gt;
        &lt;version&gt;1.0.0&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;</code>

Configure Redis and Sa-Token in

application.yml

:

<code>spring:
  redis:
    database: 0
    port: 6379
    host: localhost
    password:

# Sa-Token configuration
sa-token:
  token-name: Authorization
  timeout: 2592000
  activity-timeout: -1
  is-concurrent: true
  is-share: false
  token-style: uuid
  is-log: false
  is-read-cookie: false
  is-read-head: true</code>

Create

SaTokenConfig

to register a global filter, define authentication rules, and handle errors:

<code>@Configuration
public class SaTokenConfig {
    /** Register Sa-Token global filter */
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
            // Intercept all paths
            .addInclude("/**")
            // Exclude favicon
            .addExclude("/favicon.ico")
            // Authentication logic
            .setAuth(r -> {
                // Login authentication (all paths except login)
                SaRouter.match("/**", "/auth/user/login", StpUtil::checkLogin);
                // Permission checks for specific APIs
                SaRouter.match("/api/test/hello", () -> StpUtil.checkPermission("api:test:hello"));
                SaRouter.match("/api/user/info", () -> StpUtil.checkPermission("api:user:info"));
            })
            // Error handling
            .setError(e -> {
                ServerWebExchange exchange = SaReactorSyncHolder.getContent();
                exchange.getResponse().getHeaders().set("Content-Type", "application/json; charset=utf-8");
                return SaResult.error(e.getMessage());
            });
    }
}
</code>

Extend

StpInterface

to provide permission lists from the session:

<code>/** Custom permission interface */
@Component
public class StpInterfaceImpl implements StpInterface {
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
        return userDTO.getPermissionList();
    }
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        return null;
    }
}
</code>

micro-sa-token-auth

Next, build the authentication service with a simple login endpoint.

Add the same dependencies as the gateway (Sa-Token, Redis, common module) to

pom.xml

.

Reuse the

application.yml

configuration shown above.

Create a login controller:

<code>@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserServiceImpl userService;

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public CommonResult login(@RequestParam String username, @RequestParam String password) {
        SaTokenInfo saTokenInfo = userService.login(username, password);
        if (saTokenInfo == null) {
            return CommonResult.validateFailed("用户名或密码错误");
        }
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", saTokenInfo.getTokenValue());
        tokenMap.put("tokenHead", saTokenInfo.getTokenName());
        return CommonResult.success(tokenMap);
    }
}
</code>

Implement login logic in the service:

<code>@Service
public class UserServiceImpl {
    private List<UserDTO> userList;

    public SaTokenInfo login(String username, String password) {
        UserDTO userDTO = loadUserByUsername(username);
        if (userDTO == null) return null;
        if (!SaSecureUtil.md5(password).equals(userDTO.getPassword())) return null;
        // Successful login
        StpUtil.login(userDTO.getId());
        StpUtil.getSession().set("userInfo", userDTO);
        return StpUtil.getTokenInfo();
    }
}
</code>

Note: Sa-Token’s session is a custom implementation, not the standard HttpSession.

micro-sa-token-api

Finally, create a protected API service that returns user information and a test endpoint requiring a specific permission.

Add the same Sa-Token and Redis dependencies to

pom.xml

.

Reuse the

application.yml

configuration.

User info endpoint:

<code>@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/info")
    public CommonResult<UserDTO> userInfo() {
        UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
        return CommonResult.success(userDTO);
    }
}
</code>

Test endpoint requiring

api:test:hello

permission:

<code>@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("/hello")
    public CommonResult hello() {
        return CommonResult.success("Hello World.");
    }
}
</code>

Feature Demonstration

After starting Nacos and Redis, launch the three services (gateway, auth, API) in any order and use Postman to test the authentication flow.

Obtain a token by calling the login API through the gateway (e.g.,

http://localhost:9201/auth/user/login

).

Access the protected

/api/user/info

endpoint without a token – request is denied.

Access the same endpoint with the token – request succeeds and returns user data.

Attempt to call the permission‑protected test endpoint as user

macro

– access is denied.

Switch to user

admin

(which has

api:test:hello

permission) and call the test endpoint – access succeeds.

Conclusion

Compared with a Spring Security‑based solution, Sa-Token offers a simpler and more elegant approach. With Spring Security you must configure authentication managers, handle unauthenticated and unauthorized responses, and set up separate resource server configurations, which is cumbersome. Sa-Token requires only a gateway filter for authentication/authorization and straightforward API calls for login and permission assignment.

References

Official documentation: http://sa-token.dev33.cn/

Source Code

GitHub repository: https://github.com/macrozheng/springcloud-learning/tree/master/micro-sa-token

JavaMicroservicesAuthenticationSpring CloudauthorizationSa-Token
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login 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.