作者:后端小肥肠
🍇 我写过的文章中的相关代码放到了gitee,地址:xfc-fdw-cloud: 公共解决方案
🍊 有疑问可私信或评论区联系我。
🥑 创作不易未经允许严禁转载。
后端集成CAS单点登录:
【Spring Security系列】10分钟实现 SpringSecurity + CAS 完美单点登录方案_spring-security-cas-CSDN博客
1. 前言
当我在GitHub上看到那个刺眼的Issue------CAS前端集成谁来接?,突然意识到:在云原生和低代码的冲击下,单维度技术栈的生存空间正在急速收缩。作为Java程序员,我曾自豪于Spring生态的精通,却不得不在跨域请求和Cookie存储的迷雾中手足无措。
这种困境想必很多后端开发者都曾遇到:
前端同事请假了,产品经理却要求本周上线跨域问题排查到凌晨,却发现是前端Cookie配置问题明明是个小需求,却因为前后端分离变得异常棘手
但这也正是机遇所在:
🔑 用AI工具突破技术边界,一个人就是一个全栈团队
🚀 告别"我 只会后端"的局限 ,拥抱全栈开发的 未来
💡 在技术转型的浪潮中抢占先机,提升个人竞争力
本文将展示如何用Cursor这把技术杠杆:
- 无需系统学习前端技术栈(HTML+CSS+JS、Vue等),轻松完成CAS前端集成
- 获得"前后端通吃"的全局技术视野
- 掌握AI辅助编程的实战经验
话不多说,让我们开始这段打破技术边界的旅程...
2. CAS单点登录流程梳理(全栈角度)
CAS单点登录流程一共可以分为三大步骤:
初始访问流程
当用户首次访问前端应用时(假设url为:http://localhost:8080/onemap),前端会自动发起一个API请求到后端服务。由于用户未登录,这个请求会被 Spring Security 的配置类(SecurityConfig )拦截。接着,认证入口点(AuthenticationEntryPointImpl)会返回401未授权状态码,并在响应头中携带CAS登录地址。前端的 axios 请求拦截器检测到401响应后,会自动将页面重定向到CAS统一认证登录页面,引导用户进行登录。
这就像是一个前台接待员(前端)帮访客(用户)问后台保安(后端)能否进入,保安发现访客没有通行证(未登录),就告诉前台让访客去登记处(CAS)办理通行证。
CAS登录认证流程
当用户在CAS统一认证系统中输入用户名和密码后,CAS服务器会对这些凭证进行验证。验证通过后,CAS服务器会生成一个一次性的服务票据(Service Ticket ,简称ST )。然后,CAS服务器会将用户的浏览器重定向回我们的前端应用系统,同时在URL中附带上这个ST票据(例如:http://localhost:8080/onemap/#/login/cas?ticket=ST-xxx)。
这个过程就像是用户在统一认证中心获得了一张临时通行证,然后被引导回我们的系统入口。
Ticket验证流程
当CAS服务器将用户重定向回前端系统后,前端路由会将请求引导至专门处理CAS登录的组件(CasLogin )。该组件首先从URL中解析出CAS服务器颁发的票据(ticket )。然后,组件会调用后端的验证接口( /login/cas )并携带这个ticket。后端的CAS认证过滤器(CasAuthenticationFilter )会处理这个验证请求,验证ticket的有效性。如果验证通过,后端会创建会话并返回会话标识(JSESSIONID)给前端。最后,前端在确认登录成功后,会自动跳转到系统首页。
这个过程就像是用户拿着临时通行证(ticket)到前台(前端)登记,前台让保安(后端)验证通行证,验证通过后给用户发放正式通行证(JSESSIONID),然后引导用户进入大厅(首页)。
3. AI编程
3.1. AI编程前置工作
在使用AI进行编程之前,我们需要做好充分的准备工作。这个过程可以分为三个关键步骤:
需求分析与技术储备
-
深入理解系统功能和业务流程
-
掌握相关技术栈的基础知识
-
明确项目的技术边界和实现目标
前期流程梳理
-
详细梳理技术流程,以本文为例,需要在前期梳理CAS单点登录认证流程
-
设计数据流转和状态管理方案
技术基础要求
虽然我们使用AI来协助开发,但仍需要了解相关基础知识(注意这里只是了解,不是让你花费几个月去拼命学),以本文为例,我们需要了解:
-
前端基础:HTML、CSS、JavaScript
-
Vue.js基础概念:组件、生命周期、路由
-
开发环境:Node.js、NPM包管理
-
网络知识:HTTP协议、Cookie机制
需要强调的是,AI编程不是技术许愿池 ,而是一个强大的协作工具。它需要开发者具备足够的技术认知来进行需求分析、方案评估和问题排查。在本项目中,虽然我们使用AI实现了基于Vue2的CAS单点登录前端集成,但如果没有前端基础知识的支撑,在调试和问题排查阶段将会举步维艰。 (只是我个人实践观点,你不要用网上那些非技术人员一天用Cursor开发了一个App还上线的例子来杠我,杠就是你对)
3.2. Cursor开发实战
- 说出你的需求,你想实现什么,让他给出方案(大概率是偏离的),比如我的需求就是基于后端已有代码,采用Vue2来进行CAS单点登录集成。
- 把它给出的方案流程在你自己脑子里过一遍,不行的话需要修订:
AI会根据你提供的思路再给一版方案,我这里提示词没控制好,它直接开始写了,大家写的时候注意控制一下,方案没问题再开始写:
- 验证AI写的代码,进入前端目录(AI生成代码的目录),运行npm install安装依赖,之后运行npm run serve,先把代码跑起来看看:
我们再把后台启动一下,访问前端url的时候直接跳转到了CAS登录界面:
输入用户名和密码后,跳转到了首页,接口数据拿到了:
到此,单点登录集成完成了。(其实很多调试步骤没有被列出来,流程梳理也很费时间,这里大家自己实践就知道了)
4. 核心代码
4.1. 后端核心代码
虽然之前的文章(blog.csdn.net/c1821359022...)已经包含详尽的后端代码,但对接到前端还需要改一下,具体修改如下:
- 后端认证入口点(这里需要改一下,之前是直接重定向到CAS认证界面了,现在要返回401,由前端来跳转到CAS认证界面)
java
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Value("${cas.server}")
private String casServerUrl;
@Value("${cas.client}")
private String casClientUrl;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 构建CAS登录URL,带上service参数
String serviceUrl = casClientUrl + "/#/login/cas";
String casLoginUrl = casServerUrl + "/login?service=" + URLEncoder.encode(serviceUrl, "UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader("Location", casLoginUrl);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{"message":"未登录或登录已过期"}");
}
}
- 修改SecurityConfig配置
- 配置自定义authenticationEntryPoint(AuthenticationEntryPointImpl)
java
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.formLogin().disable()
.logout().disable()
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.antMatchers("/login/cas", "/logout/cas").permitAll()
.antMatchers("/error").permitAll()
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/v2/api-docs").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/doc.html").permitAll()
.anyRequest().authenticated())
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.addFilter(casAuthenticationFilter())
.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class)
.addFilterBefore(casLogoutFilter(), LogoutFilter.class);
return http.build();
}
- 修改ServiceProperties配置
java
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService("http://localhost:8080/onemap/#/login/cas");
serviceProperties.setAuthenticateAllArtifacts(true);
return serviceProperties;
}
setService()设置的URL就是CAS服务器回调的地址,会带上ticket参数,我们需要将其改为前端地址(我的前端地址为http://localhost:8080/onemap)。
- 修改CasAuthenticationFilter
java
@Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
// 1. 设置认证管理器
filter.setAuthenticationManager(authenticationManager());
// 2. 设置处理ticket验证的URL路径
filter.setFilterProcessesUrl("/login/cas");
// 3. 设置service配置
filter.setServiceProperties(serviceProperties());
// 4. 认证成功处理器
filter.setAuthenticationSuccessHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{"status":"200","message":"登录成功"}");
});
// 5. 认证失败处理器
filter.setAuthenticationFailureHandler((request, response, exception) -> {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader("Location", casServerUrl + "/login");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{"message":"认证失败"}");
});
return filter;
}
CAS认证过滤器(CasAuthenticationFilter ),用于处理CAS服务器回调时的ticket 验证。它设置了认证管理器来处理认证逻辑,指定/login/cas作为处理ticket验证的URL路径,并注入service配置来指定CAS回调地址。同时配置了两个关键的处理器:认证成功时返回200状态码和成功消息,并自动设置JSESSIONID;认证失败时返回401状态码,并在响应头中设置CAS登录地址以便前端重新发起登录。
4.2. 前端核心代码
- 前端拦截请求器
js
// utils/request.js
service.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
window.location.href = error.response.headers.location;
return;
}
Message.error(error.message);
return Promise.reject(error);
}
)
这段代码是前端的axios响应拦截器配置,主要用于统一处理后端接口响应。当接口请求成功时直接返回响应数据;当请求失败时,会检查是否是401未授权错误(表示用户未登录),如果是401则从响应头中获取CAS登录地址并进行页面跳转,否则显示错误消息并将Promise 设为rejected状态。
- 前端CAS回调处理
js
// views/login/CasLogin.vue
export default {
methods: {
getTicketFromUrl() {
const url = window.location.href;
const hashIndex = url.indexOf('#');
const queryString = hashIndex > -1 ? url.substring(0, hashIndex) : url;
const urlParams = new URLSearchParams(queryString.split('?')[1]);
return urlParams.get('ticket');
},
async handleLogin() {
try {
const ticket = this.getTicketFromUrl();
const response = await validateTicket(ticket);
if (response.status === 200) {
this.$router.push('/');
}
} catch (error) {
this.error = error.message;
}
}
}
}
这段代码是CAS登录回调处理组件的核心方法,主要完成两个任务:getTicketFromUrl 方法负责从URL中解析CAS服务器回调时携带的ticket参数(处理类似http://localhost:8080/onemap/#/login/cas?ticket=ST-xxx 这样的URL);handleLogin方法则负责调用后端接口验证这个ticket的有效性,如果验证成功(返回200状态码)就跳转到系统首页,验证失败则显示错误信息。
5. 源码获取
关注gzh后端小肥肠,点击底部【资源】菜单就能获取前端源码,后端源码已经很详尽,不再提供。
6. 结语
这次CAS集成的实战,远不止实现一个功能这么简单。它证明了:在AI工具的加持下,后端程序员完全能驾驭前端关键模块。
🎯 技术成长启示
1️⃣ AI不是万能的,但懂AI的程序员是万能的
2️⃣ 技术边界正在消失,全栈思维是必备技能
3️⃣学会用AI工具,让你的能力突破天际
📚你的下一步行动:
1️⃣ 立即下载源码
2️⃣ 参考往期《【Spring Security系列】10分钟实现 SpringSecurity + CAS 完美单点登录方案》,构建高可用认证体系
3️⃣ 安装Cursor,用我的方法尝试一下AI编程(你会回来谢我)
如果本文对你有帮助,请动动你发财的小手点点关注,小肥肠将持续分享更多AI干货文章~