Backend Development 15 min read

Integrating Sa-Token Authentication in Spring Cloud Alibaba Gateway and Authorization Service

This article provides a step‑by‑step guide on configuring Sa‑Token for login, permission, SSO and distributed session management in a Spring Cloud Alibaba gateway and a separate authorization service, including Maven dependencies, bootstrap settings, global filters, exception handling, role/permission interfaces, and testing procedures.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Integrating Sa-Token Authentication in Spring Cloud Alibaba Gateway and Authorization Service

Sa-Token is a lightweight Java permission authentication framework that provides login authentication, permission verification, SSO, OAuth2.0, distributed session, and microservice gateway authentication.

The article demonstrates how to configure a gateway service and an authorization service using Spring Cloud Alibaba, Spring Boot 2.1.13, Sa-Token 1.30.0, and Redis.

Gateway configuration

1. Add required dependencies in pom.xml (Sa-Token reactor starter, Redis DAO, Spring Cloud Gateway).

<!-- Sa-Token 权限认证(Reactor响应式集成) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-reactor-spring-boot-starter</artifactId>
    <version>1.30.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-dao-redis-jackson</artifactId>
    <version>1.30.0</version>
</dependency>
<!-- GateWay 网关 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

2. Add Sa-Token settings to bootstrap.yml (token name, timeout, concurrency, etc.).

# Sa-Token配置
sa-token:
  token-name: satoken
  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

3. Implement a global filter SaTokenConfigure to register SaReactorFilter with include/exclude paths, authentication logic using SaRouter , error handling, and CORS headers.

package com.frontop.meta.config;

import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.frontop.meta.util.ResultJsonUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;

@Configuration
public class SaTokenConfigure {
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
            .addInclude("/**")
            .addExclude("/favicon.ico")
            .setAuth(obj -> {
                SaRouter.match("/**", "/meta-auth/phoneLogin", r -> StpUtil.checkLogin());
                SaRouter.match("/admin/**", r -> StpUtil.checkRoleOr("admin", "super-admin"));
                SaRouter.match("/meta-system/**", r -> StpUtil.checkPermission("system-no"));
                SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
                SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
                SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
            })
            .setError(e -> {
                ServerWebExchange exchange = SaReactorSyncHolder.getContext();
                exchange.getResponse().getHeaders().set("Content-Type", "application/json; charset=utf-8");
                return SaResult.error(e.getMessage());
            })
            .setBeforeAuth(obj -> {
                SaHolder.getResponse()
                    .setHeader("Access-Control-Allow-Origin", "*")
                    .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
                    .setHeader("Access-Control-Max-Age", "3600")
                    .setHeader("Access-Control-Allow-Headers", "*");
                SaRouter.match(SaHttpMethod.OPTIONS)
                    .free(r -> System.out.println("--------OPTIONS预检请求,不做处理"))
                    .back();
            });
    }
}

4. Create a global exception handler GlobalException to map Sa‑Token exceptions to custom JSON responses.

package com.frontop.meta.config;

import cn.dev33.satoken.exception.DisableLoginException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.frontop.meta.constant.ResponseCodeConstant;
import com.frontop.meta.constant.ResponseMessageConstant;
import com.frontop.meta.util.ResultJsonUtil;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ResponseBody
@ExceptionHandler
public class GlobalException {
    @ResponseBody
    @ExceptionHandler
    public ResultJsonUtil
handlerException(Exception e) {
        e.printStackTrace();
        ResultJsonUtil
re = null;
        if (e instanceof NotLoginException) {
            re = new ResultJsonUtil().customized(ResponseCodeConstant.OAUTH_TOKEN_FAILURE, ResponseMessageConstant.OAUTH_TOKEN_MISSING, null);
        } else if (e instanceof NotRoleException) {
            NotRoleException ee = (NotRoleException) e;
            re = new ResultJsonUtil().customized(ResponseCodeConstant.OAUTH_TOKEN_DENIED, "无此角色:" + ee.getRole(), null);
        } else if (e instanceof NotPermissionException) {
            NotPermissionException ee = (NotPermissionException) e;
            re = new ResultJsonUtil().customized(ResponseCodeConstant.OAUTH_TOKEN_DENIED, "无此角色:" + ee.getCode(), null);
        } else if (e instanceof DisableLoginException) {
            DisableLoginException ee = (DisableLoginException) e;
            re = new ResultJsonUtil().customized(ResponseCodeConstant.USER_LOCK, "账号被封禁:" + ee.getDisableTime() + "秒后解封", null);
        } else {
            re = new ResultJsonUtil().fail(e.getMessage());
        }
        return re;
    }
}

5. Implement StpInterfaceImpl to provide mock role and permission lists for the logged‑in user.

package com.frontop.meta.config;

import cn.dev33.satoken.stp.StpInterface;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;

@Component
public class StpInterfaceImpl implements StpInterface {
    @Override
    public List
getPermissionList(Object loginId, String loginType) {
        List
strs = new ArrayList<>();
        strs.add("system");
        return strs;
    }
    @Override
    public List
getRoleList(Object loginId, String loginType) {
        List
strs = new ArrayList<>();
        strs.add("admin");
        return strs;
    }
}

Authorization service configuration

Repeat the same Sa‑Token dependencies and bootstrap.yml settings in the system service, then add a simple login controller using StpUtil.login and token retrieval.

package com.frontop.meta.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.frontop.meta.util.ResultJsonUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Api(tags = "用户授权登录")
public class UserLoginController {
    @ApiOperation(value = "手机+密码登录")
    @PostMapping("/phoneLogin")
    public ResultJsonUtil
getAwardCount(String phone, String password) {
        if (phone.equals("18874288923") && password.equals("123")) {
            StpUtil.login(1001, "PC");
            return new ResultJsonUtil().success(StpUtil.getTokenInfo());
        }
        return new ResultJsonUtil().fail("手机号或密码错误");
    }
}

Testing shows token generation, storage in Redis, and access control enforced by the gateway. Requests without a token or without required roles/permissions are blocked, while authorized requests succeed.

Finally, the article shows how to enable annotation‑based interception with SaAnnotationInterceptor and configure CORS in the gateway.

package com.geo.gateway.config;

import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

public class CorsConfig {
    @Bean
    public CorsWebFilter corsWebFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.addAllowedMethod("*");
        corsConfig.addAllowedOrigin("*");
        corsConfig.addAllowedHeader("*");
        corsConfig.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", corsConfig);
        return new CorsWebFilter(source);
    }
}

Source repository: https://gitee.com/yangjial/meta.git (frontop branch).

JavaMicroservicesauthenticationgatewaySpring CloudSa-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.