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.
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
UserDTOand
CommonResultmicro-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><dependencies>
<!-- Sa-Token permission (reactive) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
<!-- Sa-Token Redis integration (Jackson) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.24.0</version>
</dependency>
<!-- Redis connection pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Common module -->
<dependency>
<groupId>com.macro.cloud</groupId>
<artifactId>micro-sa-token-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies></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
SaTokenConfigto 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
StpInterfaceto 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.ymlconfiguration 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.ymlconfiguration.
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:hellopermission:
<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/infoendpoint 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:hellopermission) 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
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.