您的当前位置:首页正文

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

2024-11-30 来源:个人技术集锦

官网地址:

统一认证服务端

直接用的官网的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)
}

显示全文