Backend Development 14 min read

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.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Integrating Sa-Token with Spring Boot for Login Authentication and Permission Verification

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/*.xml

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

backendJavaSpring BootauthenticationauthorizationSa-Token
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.