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<Object> handlerException(Exception e) {
        e.printStackTrace();
        ResultJsonUtil<Object> 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<String> getPermissionList(Object loginId, String loginType) {
        List<String> strs = new ArrayList<>();
        strs.add("system");
        return strs;
    }
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        List<String> 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<Object> 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).

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaMicroservicesSpring 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

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.