【工作记录】前后端分离场景下CAS单点登录的集成思路与实践@20230926
背景及目的
之前做一个公司项目的时候甲方要求集成他们指定的CAS服务端实现登录,要求不影响原有业务。
CAS服务端提供的文档都是基于前后端不分离的应用,对前后端分离应用没有任何说明,找官方人问也是爱答不理的,近期正好有时间就想着研究下这个集成过程。
于是有了这篇文章,主要为了记录下集成过程和相关配置,方便后续类似的对接。
当然也希望能帮助到需要的朋友。
CAS涉及到的角色
- 认证服务器
- 客户端-API
- 客户端-前台
整体流程梳理:
- 客户端请求地址A
- 发现用户没有token,跳转到指定登录页面,地址类似/cas/login?service=xxxxx
- 请求成功后会回调callbackUrl同时拼接ticket参数,
- 客户端发起ticket验证请求,v2: /serviceValidate v3: /p3/serviceValidate
- 验证成功后会回调到callbackUrl中,同时在session中添加指定用户属性
- 生成token并传递到前端或前端通过请求拿到token
- 思路一: 客户端拿到用户属性后根据用户生成token返回给前端,可以通过指定url拼接参数给到前端,前端拦截后保存
- 思路二: 客户端拿到用户属性后重定向到一个约定的前端地址,前端在路由守卫中拦截该路径并在拦截到后发起getToken请求,请求成功后
保存token等信息到缓存或者cookie即可。
实现过程
前期准备
- cas认证服务端部署
- 注册CAS协议的应用,需要配置service/callbackUrl/clientName
- vue-admin-template模板项目
- 简单的springboot项目
- 配置几个用于跳转的路由,在路由文件中添加
{path: '/callback',hidden: true},{path: '/tologin',hidden: true},
这里只是为了路由守卫的拦截,可以不写vue页面,只需要在路由列表中添加定义即可,亲测无误。
改造过程-前端
路由守卫文件 src/permission.js
import {getToken} from '@/utils/auth'; // get token from cookie
import getPageTitle from '@/utils/get-page-title'
import NProgress from 'nprogress'; // progress bar
import 'nprogress/nprogress.css'; // progress bar style
import router from './router'
import store from './store'NProgress.configure({ showSpinner: false }) // NProgress Configurationconst whiteList = ['/login'] // no redirect whitelist//是否支持cas登录的开关
const enableCasLogin = truerouter.beforeEach(async(to, from, next) => {// start progress barNProgress.start()// set page titledocument.title = getPageTitle(to.meta.title)const hasToken = getToken()//判断是否是去login页面if(to.path === '/tologin') {if(enableCasLogin){//实际访问的cas登录地址window.location.href = `http://localhost:9009/cas/login?service=http://localhost:8989/test1/index&redirect=${to.params.redirect}`} else {next(`/login`)}NProgress.done()} else if (whiteList.indexOf(to.path) !== -1) {next();} else if (to.path === '/callback') {if(!hasToken) {await store.dispatch(`user/resetToken`)}next('/')NProgress.done()} else {if(hasToken) {next()} else {if(to.path === '/') {next(`/tologin`)} else {next(`/tologin?redirect=${to.path}`)}}NProgress.done()}
})router.afterEach(() => {// finish progress barNProgress.done()
})
接口配置 /api/user.js
import request from '@/utils/request'export function login(data) {return request({url: '/auth/login',method: 'post',data})
}export function getCaptcha() {return request({url: '/auth/captcha',method: 'get',})
}export function getCasToken() {return request({url: '/auth/getToken',method: 'get',})
}export function logout() {return request({url: '/vue-admin-template/user/logout',method: 'post'})
}
store配置 src/store/modules/user.js
import { getCasToken, login, logout } from '@/api/user'
import { resetRouter } from '@/router'
import { getToken, removeToken, setToken } from '@/utils/auth'const getDefaultState = () => {return {token: getToken(),name: '',avatar: ''}
}const state = getDefaultState()const mutations = {RESET_STATE: (state) => {Object.assign(state, getDefaultState())},SET_TOKEN: (state, token) => {state.token = token},SET_NAME: (state, name) => {state.name = name},SET_AVATAR: (state, avatar) => {state.avatar = avatar}
}const actions = {// user loginlogin({ commit }, userInfo) {return new Promise((resolve, reject) => {login(userInfo).then(response => {const { data } = responsecommit('SET_TOKEN', data.tokenType + " " + data.token)setToken(data.token)resolve()}).catch(error => {reject(error)})})},// user logoutlogout({ commit, state }) {return new Promise((resolve, reject) => {logout(state.token).then(() => {removeToken() // must remove token firstresetRouter()commit('RESET_STATE')resolve()}).catch(error => {reject(error)})})},// remove tokenresetToken({ commit }) {return new Promise(resolve => {removeToken() // must remove token firstcommit('RESET_STATE')getCasToken().then(response => {const { data } = responsecommit('SET_TOKEN', data.tokenType + " " + data.token)commit('SET_NAME', data.user.username)setToken(data.token)resolve()}).catch(error => {reject(error)})})}
}export default {namespaced: true,state,mutations,actions
}
配置下接口地址 src/vue.config.js
module.exports = {/*** You will need to set publicPath if you plan to deploy your site under a sub path,* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,* then publicPath should be set to "/bar/".* In most cases please use '/' !!!* Detail: https://cli.vuejs.org/config/#publicpath*/publicPath: '/',outputDir: 'dist',assetsDir: 'static',lintOnSave: process.env.NODE_ENV === 'development',productionSourceMap: false,devServer: {port: port,open: true,overlay: {warnings: false,errors: true},// before: require('./mock/mock-server.js')proxy: {'/dev-api': {target: 'http://localhost:8989',pathRewrite: { '^/dev-api': '' }}}},configureWebpack: {// provide the app's title in webpack's name field, so that// it can be accessed in index.html to inject the correct title.name: name,resolve: {alias: {'@': resolve('src')}}},chainWebpack(config) {// it can improve the speed of the first screen, it is recommended to turn on preloadconfig.plugin('preload').tap(() => [{rel: 'preload',// to ignore runtime.js// https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],include: 'initial'}])// when there are many pages, it will cause too many meaningless requestsconfig.plugins.delete('prefetch')// set svg-sprite-loaderconfig.module.rule('svg').exclude.add(resolve('src/icons')).end()config.module.rule('icons').test(/\.svg$/).include.add(resolve('src/icons')).end().use('svg-sprite-loader').loader('svg-sprite-loader').options({symbolId: 'icon-[name]'}).end()config.when(process.env.NODE_ENV !== 'development',config => {config.plugin('ScriptExtHtmlWebpackPlugin').after('html').use('script-ext-html-webpack-plugin', [{// `runtime` must same as runtimeChunk name. default is `runtime`inline: /runtime\..*\.js$/}]).end()config.optimization.splitChunks({chunks: 'all',cacheGroups: {libs: {name: 'chunk-libs',test: /[\\/]node_modules[\\/]/,priority: 10,chunks: 'initial' // only package third parties that are initially dependent},elementUI: {name: 'chunk-elementUI', // split elementUI into a single packagepriority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or apptest: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm},commons: {name: 'chunk-commons',test: resolve('src/components'), // can customize your rulesminChunks: 3, // minimum common numberpriority: 5,reuseExistingChunk: true}}})// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunkconfig.optimization.runtimeChunk('single')})}
}
改造过程-后端
添加依赖 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.13</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.zjtx.tech</groupId><artifactId>auth-cas-api</artifactId><version>0.0.1-SNAPSHOT</version><name>auth-cas-api</name><description>CAS API</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>net.unicon.cas</groupId><artifactId>cas-client-autoconfig-support</artifactId><version>2.3.0-GA</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
依赖中主要添加的是cas-client-autoconfig-support
这个jar包,提供了cas需要的一些配置和拦截器
配置文件添加
server:port: 8989
cas:# 配置实际的cas地址server-url-prefix: http://localhost:9009/cas# 配置实际的cas登录地址server-login-url: http://localhost:9009/cas/loginclient-host-url: http://localhost:8989/# 这里可以选择cas 和 cas3 区别是请求的部分地址不一样,如ticket验证的接口validation-type: cas
# 拦截的URL地址authentication-url-patterns:- /*
spring:jackson:serialization:FAIL_ON_EMPTY_BEANS: false
获取token的controller
src/main/java/com/zjtx/tech/controller/AuthController.java
package com.zjtx.tech.contorller;import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.Assertion;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@RestController
@RequestMapping("/auth")
public class AuthController {@GetMapping("getToken")public ResultBean<TokenUser> getToken(HttpServletRequest request){Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);System.out.println("assertion = " + assertion.getPrincipal().getAttributes());String username = assertion.getPrincipal().getName();System.out.println(username);//这里仅为了演示直接new了一个简单对象返回给前端SimpleUserBean user = new SimpleUserBean("1", username, "123456", "456789");return new ResultBean<>(200, "success", new TokenUser(user, "123456", "Bearer"));}}
回调接口对应的controller
src/main/java/com/zjtx/tech/controller/TestController.java
package com.zjtx.tech.contorller;import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.Assertion;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@RestController
public class TestController {@GetMapping("test1/index")public void index(HttpServletRequest request, HttpServletResponse resp) throws IOException {String token = request.getParameter("congress");System.out.println("congress : " + token);Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);System.out.println("assertion = " + assertion.getPrincipal().getAttributes());String username = assertion.getPrincipal().getName();System.out.println(username);resp.sendRedirect("http://localhost:9528/#/callback");}@GetMapping("test1/index1")public String index1(HttpServletRequest request) {String token = request.getParameter("token");System.out.println("token : " + token);Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);String username = assertion.getPrincipal().getName();System.out.println(username);return "test index cas拦截正常,登录账号:" + username;}/*** 不走cas认证,无法获取登录信息** @param request* @return*/@GetMapping("test1/index2")public String index2(HttpServletRequest request) {return "cas 未拦截";}
}
涉及到的简单bean就不在此列举了。
测试过程
- 启动前后端及认证服务器项目
- 访问前端地址 会跳转到认证服务器定义的loginUrl
- 登录完成后会调用http://localhost:9528/#/callback,被路由守卫拦截后进行token的获取和保存
- 保存完成后进入首页
总结
本文主要记录了前后端分离场景下集成CAS单点登录的基本流程。
作为记录的同时也希望能帮助到需要的朋友,有任何疑问欢迎留言评论。
创作不易,欢迎一键三连~
相关文章:
【工作记录】前后端分离场景下CAS单点登录的集成思路与实践@20230926
背景及目的 之前做一个公司项目的时候甲方要求集成他们指定的CAS服务端实现登录,要求不影响原有业务。 CAS服务端提供的文档都是基于前后端不分离的应用,对前后端分离应用没有任何说明,找官方人问也是爱答不理的,近期正好有时间就…...

阿里云数据库RDS有哪些?细数关系型数据库大全
阿里云RDS关系型数据库大全,关系型数据库包括MySQL版、PolarDB、PostgreSQL、SQL Server和MariaDB等,NoSQL数据库如Redis、Tair、Lindorm和MongoDB,阿里云百科分享阿里云RDS关系型数据库大全: 目录 阿里云RDS关系型数据库大全 …...

【计算机网络】因特网中的电子邮件
文章目录 简单邮件传送协议SMTP邮件访问协议POP3IMAPHTTP 参考资料 电子邮件为异步通信媒介 因特网电子邮件系统 电子邮件系统的三个构件:用户代理、邮件服务器、邮件发送和读取协议 用户代理 User Agent 即UA 电子邮件客户端软件,用户与电子邮件系统的接…...

【C++11】多线程
多线程创建线程thread提供的成员函数获取线程id的方式线程函数参数的问题线程join场景和detach 互斥量库(mutex)mutexrecursive_mutexlock_guard 和 unique_lock 原子性操作库(atomic)条件变量库(condition_varuable&a…...

【vue3】shallowReactive与shallowRef;readonly与shallowReadonly;toRaw与markRaw
假期第六篇,对于基础的知识点,我感觉自己还是很薄弱的。 趁着假期,再去复习一遍 1、shallowReactive与shallowRef shallowReactive:只处理对象最外层属性的响应式(浅响应式) shallowRef:只处理…...

手机建模教程 | 如何从易模App中导出模型?有哪些格式?含贴图吗?
很多小伙伴使用易模App是为了能快速地将已有实物的物体“变成”三维模型后转到自己习惯的3D软件中去编辑,于是,大家都关心模型能否导出,以及导出格式有没有自己想要的? 博雅仔告诉大家,当然可以导出! 在导出…...
数据分析技能点-机器学习优化思想
优化思想,这个听起来极其专业和高端的词汇,其实它无处不在,悄无声息地影响着我们的生活和决策。从寻找最快的上班路线,到决定如何配置投资组合,优化思想都是一个不可或缺的元素。而在机器学习领域,优化思想更是扮演着至关重要的角色。 文章目录 优化的基础优化问题与实际…...

应用架构的演进:亚马逊的微服务实践
当你在亚马逊上购物时,或许不会想到,你看到的这个购物网站,其背后技术架构经历了什么样的变迁与升级。 还记得上世纪 90 年代,那个只卖书的网上书店吗?那时的亚马逊,不过是一个架构简单的网站,所有的功能都堆积在一个庞大的软件堡垒里。随着更多业务的增加、更新和迭代,这个软…...

leetCode 55.跳跃游戏 贪心算法
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。 示例 1: 输入…...

CF505B Mr. Kitayuta‘s Colorful Graph
Mr. Kitayuta’s Colorful Graph 题面翻译 给出一个 n n n 个点, m m m 条边的无向图,每条边上是有颜色的。有 q q q 组询问 对于第 i i i 组询问,给出点对 u i , v i u_i,v_i ui,vi。求有多少种颜色 c c c 满足:有至…...

c#设计模式-结构型模式 之 组合模式
🚀简介 组合模式又名部分整体模式,是一种 结构型设计模式 ,是用于把一组相似的对象当作一个 单一的对象 。组合模式 依据树形结构来组合对象 ,用来表示部分以及整体层,它可以让你将对象组合成树形结构,并且…...
【Rust日报】2023-09-30 使用Rust做web抓取
CockroachDB 用rust重新实现 嘿,伙计们,我在 Rust 中实现了一个分布式 SQL 数据库。它就像 CockroachDB 和谷歌Google Spanner。告诉我你的想法。 注意: 这不是生产级别的数据库,这是一个以学习为目的的项目。有许多特性,但是缺少…...

【密评】商用密码应用安全性评估从业人员考核题库(三)
商用密码应用安全性评估从业人员考核题库(三) 国密局给的参考题库5000道只是基础题,后续更新完5000还会继续更其他高质量题库,持续学习,共同进步。 501 多项选择题 《个人信息保护法》要求个人信息处理者应当采取哪些…...
MySQL进阶_查询优化和索引优化
文章目录 第一节、索引失效案例1.1 数据准备1.2 全值匹配我最爱1.3 最佳左前缀法则 第一节、索引失效案例 可以从以下维度对数据库进行优化: 索引失效、没有充分利用到索引–索引建立关联查询太多JOIN (设计缺陷或不得已的需求)–SQL优化服务器调优及各个参数设置…...
Hadoop2复安装过程详细步骤
1、在vmware中更改了虚拟机的网络类型,--->NAT方式,(虚拟交换机的ip可以从vmvare的edit-->vertual network editor看到) 2、根据这个交换机(网关)的地址,来设置我们的客户端windows7的ip&…...
【Java-LangChain:面向开发者的提示工程-7】文本扩展
第七章 文本扩展 扩展是将短文本(例如一组说明或主题列表)输入到大型语言模型中,让模型生成更长的文本(例如基于某个主题的电子邮件或论文)。这种应用是一把双刃剑,好处例如将大型语言模型用作头脑风暴的伙…...

竞赛 基于设深度学习的人脸性别年龄识别系统
文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习机器视觉的…...

从技能需求到就业前景,了解前端和后端开发的优缺点和个人选择
文章目录 每日一句正能量一、引言前端开发后端开发 二、两者的对比分析三、技能转换和跨领域工作四:介绍全栈开发后记 每日一句正能量 命运决定的不是你的人生,能决定你人生的只有自己。 一、引言 前端和后端是Web开发中两个不可或缺的领域。前端开发主…...

Flutter笔记:AnimationMean、AnimationMax 和 AnimationMin 三个类的用法
Flutter笔记 AnimationMean、AnimationMax 和 AnimationMin三个类的用法 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/…...

华为云云耀云服务器L实例评测|云耀云服务器L实例部署Gogs服务器
华为云云耀云服务器L实例评测|云耀云服务器L实例部署Gogs服务器 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、Gogs介绍2.1 Gogs简介2.2 Gogs特点 三、本次实践介绍3.1 本次实践简介3.2 本次环境规划 四、远程登录华为云云耀云…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...

C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
【Java】Ajax 技术详解
文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...