Java + sa-token统一身份认证开发笔记

官网地址:Sa-Token

统一认证服务端

直接用的官网的demo,稍加改动,因为要前后端分离,加了一个H5Controller,官网也有详细介绍,这一部分不难,照着做就行了

配置文件:

# Sa-Token 配置
sa-token:
    # ------- SSO-模式一相关配置  (非模式一不需要配置)
    # cookie:
         # 配置 Cookie 作用域
         # domain: stp.com

    # ------- SSO-模式二相关配置
    sso:
        # Ticket有效期 (单位: 秒),默认五分钟
        ticket-timeout: 300
        # 所有允许的授权回调地址
        allow-url: "*"

        # ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开)
        # 是否打开模式三
        is-http: true
    sign:
        # API 接口调用秘钥
        secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor

        # ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)

业务服务端

因为要用到跨redis,跨域,所以要用到他们的client3模式。

配置文件

sa-token:
    # SSO-相关配置
    sso:
        # SSO-Server端 统一认证地址
        auth-url: http://localhost:9000/sso/auth
        #auth-url: http://127.0.0.1:5500/sso-login.html
        # 使用 Http 请求校验ticket (模式三)
        is-http: true
        # SSO-Server端 ticket校验地址
        check-ticket-url: http://localhost:9000/sso/checkTicket
        # 单点注销地址
        slo-url: http://localhost:9000/sso/signout
        # 查询数据地址
        get-data-url: http://localhost:9000/sso/getData
    sign:
        # API 接口调用秘钥
        secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor

上面是demo,整合到自己的业务服务端

将自己的token一并返回给前端

	@RequestMapping("/sso/doLoginByTicket")
	public SaResult doLoginByTicket(String ticket) {
		Object loginId = SaSsoProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket");
		if(loginId != null) {
			StpUtil.login(loginId);
			String token = jwtConfig.createToken(loginId.toString()) ;
			Map<String, Object> resultMap = new HashMap<>();
			resultMap.put("satoken", StpUtil.getTokenValue());
			resultMap.put("token", token);
			return SaResult.data(resultMap);
		}
		return SaResult.error("无效ticket:" + ticket);
	}

提供一个api获取登录后的相关信息

    @RequestMapping("/loginBySso")
    public Result<?> loginBySso(HttpServletRequest request) {

        String token = request.getHeader("token");
        String userId = jwtConfig.getUsernameFromToken(token);

        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", userId);
        SysUser sysUser = sysUserService.getOne(queryWrapper, false);
        if (sysUser != null) {
            if(sysUser.getLockFlag()!=null && sysUser.getLockFlag().equals("1")){
                return Result.error("用户已被锁定,请联系管理员解锁!",sysUser);
            }
            if(!"0".equals(sysUser.getStatus())){
                if(DBsUserConstant.Status.SECPERIOD.equals(sysUser.getStatus())){
                    return Result.error("用户账号已删除,请联系管理员!",sysUser);
                }else {
                    return Result.error("用户账号正在申请中,请联系管理员审核!", sysUser);
                }
            }
            LoginModel model = getUserInfo(sysUser);
            return Result.ok(JSON.toJSONString(model));
        } else {
            return Result.error("无此用户!");
        }
    }

业务客户端

参考的这个demo

整合到自己的业务客户端

修改路由配置,非常关键

router.beforeEach(async (to, from, next) => {

  //debugger;
  let token = sessionStorage.getItem('token');
  const url = new URL(window.location.href)
  const params = getParams(url);
  let ticket = params.ticket;
  if (ticket && isEmpty(token)) {
    //debugger;
    //有票据拿token
    const ticketRes = await ssoLoginByTicket({ticket: ticket})
    sessionStorage.setItem('token', ticketRes.data.data.token)
    localStorage.setItem('satoken', ticketRes.data.data.satoken)
    const res = await userLoginBySso()
    afterLogin(res);
    config.configData.sso_enable = true
    next({ path: desktopUrl() })
    return false
  } else {
    if (to.path === '/sso')  {
      config.configData.sso_enable = true
    } 
    //无票据,进入登录页面
    if (config.configData.sso_enable && isEmpty(token)) {
      const ssoUrlRes = await ssoAuthUrl({clientLoginUrl: location.origin+'/'})
      location.href = ssoUrlRes.data.data
      return false;
    }
    //如果直接跳登录页面,不管是不是sso都眺
    if (to.path === '/login') {
      next()
      return false
    }
    //跳首页时直接决定是登录页还是桌面
    if (to.path === '/' ||  to.path === '/sso') {
      if (isEmpty(token)) {
        next("login")
      } else {
        next("desktop")
      }
      return false
    }
    //不能反着跳
    if (to.path  === "/" && from.path === "/login") {
      return false;
    }    
  }  
  next()
})

相关api

import Vue from 'vue'
import {ajax} from '@/utils/httputils'
import config from '@/config/index.js'

const API_URL = config.configData.api_url
const http = (method, url, data) => {
  return new Promise((resolve, reject) => {
    return ajax(method, url, data).then((res) => {
      if (res.data.code == 200) {
        resolve(res)
      } else {
        reject(res)
      }
    }).catch(error => reject(error))
  })
}
/**
 * server
 * /sso/auth
 * /sso/checkTicket
 * /sso/signout
 * /sso/userinfo
 *
 * client
 * /sso/getSsoAuthUrl
 * /sso/doLoginByTicket
 * /sso/logout
 * /sso/logoutCall
 */
export function ssoAuth(data) {
  return http('get', API_URL + "/sso/auth", data)
}
export function ssoCheckTicket(data) {
  return http('get', API_URL + "/sso/checkTicket", data)
}
export function ssoSignout(data) {
  return http('get', API_URL + "/sso/signout", data)
}
export function ssoUserInfo(data) {
  return http('get', API_URL + "/sso/userinfo", data)
}


export function ssoAuthUrl(data) {
  return http('get', API_URL + "/sso/getSsoAuthUrl", data)
}
export function ssoLoginByTicket(data) {
  return http('get', API_URL + "/sso/doLoginByTicket", data)
}
export function ssoLogout(data) {
  return http('get', API_URL + "/sso/logout", data)
}
export function ssoLogoutCall(data) {
  return http('get', API_URL + "/sso/logoutCall", data)
}

<think>好的,我现在要帮用户解决在Java项目中集成sa-token与uni-app实现单点登录的问题。首先,我需要回忆一下sa-token的相关知识。用户提到了引用[1]中的SaTokenEventCenter.registerListener,这说明sa-token有事件监听机制,可能在登录时触发某些操作。另外,用户还提到Hutool工具库,但暂时可能和SSO关系不大,先不考虑。 单点登录(SSO)的核心是多个系统共享登录状态。常见的方案有基于Token的SSO,比如用户登录一个系统后,生成一个全局的Token,其他系统验证这个Token的有效性。sa-token支持分布式会话和Token认证,所以应该可以用它来实现。 接下来,我需要确定sa-token的集成步骤。首先,用户需要在Java后端引入sa-token的依赖,比如Maven的配置。然后,配置sa-token,特别是设置不同的系统域名,因为SSO需要共享Cookie,而跨域情况下需要处理。可能需要设置Cookie的作用域为顶级域名,比如.example.com,这样所有子域名都可以访问。 然后是单点登录的逻辑。用户登录主系统后,sa-token会生成Token,并存储在Cookie中。其他子系统需要验证这个Token,所以需要有一个统一的认证中心。或者,sa-token可能已经内置了SSO的模块,需要查证一下。比如,sa-tokensa-token-sso模块,可以简化SSO的实现。 对于uni-app前端,如何处理跨域的问题?uni-app使用JavaScript,可能需要在前端请求时携带Token。如果使用Cookie,需要确保跨域请求允许携带凭证,比如设置withCredentials为true。或者,使用Token放在请求头中,每次请求时传递给后端。 另外,需要考虑安全性,比如Token的加密传输、HTTPS的使用,防止中间人攻击。同时,Token的有效期和刷新机制也需要处理,避免用户频繁登录。 用户可能遇到的难点包括跨域Cookie的设置、Token的传递方式、以及各个子系统之间的会话同步。需要分步骤说明如何配置sa-token,如何在前端uni-app中处理登录状态,以及后端如何验证Token。 可能还需要示例代码,比如后端的Controller处理登录和验证,前端uni-app的登录跳转和Token传递。例如,主系统登录后生成Token,重定向到子系统时携带Token参数,子系统后端向认证中心验证Token的有效性。 最后,要确保引用用户提供的参考内容,比如SaTokenEventCenter的使用,可能在事件监听时记录日志或执行其他操作。相关问题部分需要生成与实现方案相关的问题,比如如何处理跨域、安全性措施、Token刷新等。</think>### Java项目中集成sa-token与uni-app实现单点登录(SSO)方案 #### 1. 核心原理 单点登录(SSO)允许用户通过一次登录访问多个互信系统。基于sa-token的实现方案主要依赖以下机制: - **Token共享**:用户登录后生成全局Token,所有子系统通过验证该Token实现状态同步[^1]。 - **跨域Cookie/Header传递**:通过统一域名或跨域技术(如CORS)实现Token传递。 #### 2. 后端集成sa-token **步骤1:添加依赖** ```xml <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.34.0</version> </dependency> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-sso</artifactId> <version>1.34.0</version> </dependency> ``` **步骤2:配置SSO参数** ```java @Configuration public class SaTokenConfig { @Autowired private void configSaToken(SaTokenConfig config) { // 设置共享域名(需统一主域名) config.setCookieDomain(".example.com"); // 启用SSO模式 SaSsoManager.config() .setAuthUrl("/sso/auth") // 认证中心地址 .setCheckTicketUrl("/sso/checkTicket"); // Token校验地址 } } ``` **步骤3:实现认证中心接口** ```java @RestController public class SsoController { // 单点登录认证接口 @RequestMapping("/sso/auth") public Object ssoAuth(String redirectUrl) { if(StpUtil.isLogin()) { String token = StpUtil.getTokenValue(); return SaResult.ok().setData(token); } else { return SaResult.code(401).setMsg("未登录"); } } // Token校验接口 @RequestMapping("/sso/checkTicket") public SaResult checkTicket(String token) { return StpUtil.checkLogin(token) ? SaResult.ok() : SaResult.error("无效Token"); } } ``` #### 3. uni-app前端适配 **方案1:通过URL参数传递Token** ```javascript // 登录跳转逻辑 uni.request({ url: 'https://sso.example.com/login', success: (res) => { if(res.data.token) { uni.navigateTo({ url: '/subsystem?token=' + res.data.token }); } } }); ``` **方案2:跨域Cookie自动携带** ```javascript // 需要配置withCredentials uni.request({ url: 'https://api.example.com/checkLogin', withCredentials: true // 允许携带Cookie }); ``` #### 4. 安全性增强措施 - **HTTPS强制使用**:防止Token被截获 - **Token加密传输**:使用JWT格式携带签名 - **短期Token有效期**:通过refreshToken机制续期 ```java // 监听登录事件设置Token有效期[^1] SaTokenEventCenter.registerListener(new SaTokenListenerForSimple() { @Override public void doLogin(String loginType, Object loginId, SaLoginModel loginModel) { StpUtil.getTokenSession().setTimeout(30 * 60); // 30分钟有效期 } }); ``` #### 5. 完整流程示例 1. 用户访问uni-app子系统A 2. 跳转至SSO认证中心验证登录状态 3. 已登录则返回Token,否则进入登录页 4. 携带Token重定向回子系统A 5. 子系统A向认证中心验证Token有效性 6. 验证通过后建立局部会话
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是唐赢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值