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 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:
<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>Configure Redis and Sa-Token in application.yml:
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: trueCreate SaTokenConfig to register a global filter, define authentication rules, and handle errors:
@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());
});
}
}Extend StpInterface to provide permission lists from the session:
/** 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;
}
}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:
@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);
}
}Implement login logic in the service:
@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();
}
}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:
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/info")
public CommonResult<UserDTO> userInfo() {
UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
return CommonResult.success(userDTO);
}
}Test endpoint requiring api:test:hello permission:
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
public CommonResult hello() {
return CommonResult.success("Hello World.");
}
}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
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
