Integrating Sa-Token with Spring Boot for Login Authentication and Permission Verification
This tutorial explains how to integrate the Sa-Token authentication framework into a Spring Boot project, covering Maven dependencies, YAML settings, global filter configuration, permission and role retrieval, WebMvc adjustments, and testing the login and authorization flow.
This article introduces Sa-Token, a lightweight Java authentication framework, and demonstrates how to integrate it into a Spring Boot application for login verification and permission checking.
Official website and features
https://sa-token.dev33.cn/doc/index.html#/
Sa-Token aims to simplify the complex configuration of Shiro and Spring Security, providing an easy‑to‑use solution.
Spring Boot integration
1. Maven dependencies
<!-- Sa-Token permission authentication, online docs: http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.25.0</version>
</dependency>
<!-- Sa-Token integration with Redis (uses JDK default serialization) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis</artifactId>
<version>1.25.0</version>
</dependency>2. YAML configuration
server:
port: 8010
spring:
servlet:
multipart:
enabled: true
location: C:/var/guoheng/picture/
max-file-size: 10MB
max-request-size: 10MB
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/fire_control?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
# Druid pool settings ... (omitted for brevity)
redis:
host: 127.0.0.1
port: 6379
password:
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
time-between-eviction-runs: 30000
sa-token:
token-name: satoken
timeout: 2592000
activity-timeout: 3600
is-concurrent: true
is-share: false
token-style: simple-uuid
is-log: false
mybatis:
mapper-locations: classpath*:mapper/*.xml3. Global Sa-Token filter configuration
package com.demo.app.config.satoken;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import result.Result;
import java.util.Arrays;
/**
* @program fire
* @description Sa-Token global filter configuration
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
// Intercept all routes, exclude none
.addInclude("/**").addExclude()
// Authentication function executed on each request
.setAuth(r -> {
System.out.println("---------- sa global auth");
SaRouter.match(Arrays.asList("/**"), Arrays.asList(
"/login",
"/druid/**",
"/default/**",
"/",
"/swagger-ui.html",
"/swagger-resources/**",
"swagger/**",
"/webjars/**",
"/swagger-ui.html/*",
"/swagger-resources",
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/**/*.svg",
"/**/*.ico",
"/**/*.png",
"/**/*.jpg",
"/**/*.xlsx",
"/**/*.docx",
"/**/*.pdf",
"/webSocket/**",
"/*/api-docs",
"/v2/api-docs-ext"
), StpUtil::checkLogin);
// Distinguish routes for permission checks
SaRouter.match("/user", () -> StpUtil.checkPermission("0001"));
SaRouter.match("/user/get/{id}", () -> StpUtil.checkPermission("001101"));
})
// Error handling
.setError(e -> Result.failure(e.getMessage()))
// Pre‑auth processing (e.g., security headers)
.setBeforeAuth(r -> {
SaHolder.getResponse()
.setServer("sa-server")
.setHeader("X-Frame-Options", "SAMEORIGIN")
.setHeader("X-Content-Type-Options", "nosniff");
});
}
}4. Permission and role retrieval implementation
package com.demo.app.config.satoken;
import cn.dev33.satoken.stp.StpInterface;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.demo.app.mapper.permission.PermissionMapper;
import com.demo.app.mapper.permission.RolePermissionMapper;
import com.demo.app.mapper.role.RoleMapper;
import com.demo.app.mapper.user.UserMapper;
import com.demo.app.mapper.user.UserRoleMapper;
import model.entity.sys.RolePermission;
import model.entity.sys.SysPermission;
import model.entity.sys.SysRole;
import model.entity.sys.UserRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* @program fire
* @description Assign permissions and roles after user login
*/
@Component
public class StpInterfaceImpl implements StpInterface {
@Autowired
UserMapper userMapper;
@Autowired
UserRoleMapper userRoleMapper;
@Autowired
RoleMapper roleMapper;
@Autowired
PermissionMapper permissionMapper;
@Autowired
RolePermissionMapper rolePermissionMapper;
@Override
public List
getPermissionList(Object userId, String s) {
// Find user roles
QueryWrapper
userRoleQueryWrapper = new QueryWrapper<>();
userRoleQueryWrapper.eq("user_id", userId);
List
userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
// Find permissions for those roles
QueryWrapper
rolePermissionQueryWrapper = new QueryWrapper<>();
rolePermissionQueryWrapper.in("role_id", userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList()));
List
rolePermissions = rolePermissionMapper.selectList(rolePermissionQueryWrapper);
QueryWrapper
permissionQueryWrapper = new QueryWrapper<>();
permissionQueryWrapper.in("id", rolePermissions.stream().map(RolePermission::getPermissionId).distinct().collect(Collectors.toList()));
List
sysPermissions = permissionMapper.selectList(permissionQueryWrapper);
return sysPermissions.stream().map(SysPermission::getCode).distinct().collect(Collectors.toList());
}
@Override
public List
getRoleList(Object userId, String s) {
// Find user roles
QueryWrapper
userRoleQueryWrapper = new QueryWrapper<>();
userRoleQueryWrapper.eq("user_id", userId);
List
userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
// Retrieve role details
QueryWrapper
sysRoleQueryWrapper = new QueryWrapper
().in("id", userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList()));
List
sysRoles = roleMapper.selectList(sysRoleQueryWrapper);
return sysRoles.stream().map(SysRole::getRoleName).distinct().collect(Collectors.toList());
}
}5. Resolving WebMvc configuration conflicts
package com.demo.app.config.webmvc;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* CORS and static resource configuration
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${spring.servlet.multipart.location}")
private String uploadFileUrl;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "DELETE", "OPTIONS")
.maxAge(3600)
.allowCredentials(true)
.allowedHeaders("*");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Static files
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
// Swagger UI
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
// Uploaded files
registry.addResourceHandler("/file/**").addResourceLocations("file:/" + uploadFileUrl);
}
}6. Testing the authentication flow
After starting the application, obtain a token from the login endpoint, place it in the request header, and access protected APIs. The screenshots below show responses when the token is missing, when the user lacks permission, and when the user has the required permission.
Without a token, the request is rejected.
When the user does not have the required permission, an error is returned.
When the permission is granted, the request succeeds.
Note: After a user logs in, the framework can automatically retrieve the token from the session, but if multiple users log in sequentially, the last token is used for all subsequent requests, so it is recommended to always send the token explicitly in the header.
For further learning, refer to the linked Java advanced video resources.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.