⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/u013266867 「明晓默」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

背景

这个问题也花了一两天解决,写到这里,记录下来,同时也希望可以帮到别人,如有疑问或许改正的地方,还望不吝赐教!

项目背景是公司有多套后台管理系统,都是jsp或者thymeleaf前后端代码在一起,接入的cas,实现一处登录多处使用。

后业务发展,需要对其进行前后端分离,前端项目由vue完成,后台不同的管理模块分别写在不同的服务里。同时涉及到,新旧系统并存,jsp平台登录后,前后端分离项目免登录,同时解决跨域问题。

CSDN有几遍介绍前后端分离接入cas的文章,都不尽完善,或者功能没有完美实现。本文主要介绍了cas-server部署成功后,cas-client的接入,引用net.unicon.cas依赖,代码侵入少,无需在filter上进行大量的自定义代码。

最后可以达到,用户登录了老的jsp项目后,在新的前后端分离项目中,可以成功请求后台接口,无需重新登录或刷新。同时也可以实现,不同的cas-client之间共享session。

改造需要注意的点

  • cookie的携带
  • 不同服务之间cookie的共享
  • clien-server授权后的页面跳转
  • 跨域的配置

普通的cas接入

pom中引入依赖

<dependency>
<groupId>net.unicon.cas</groupId>
<artifactId>cas-client-autoconfig-support</artifactId>
<version>2.3.0-GA</version>
</dependency>

配置cas-server地址,本地地址,validation-type(cas,cas3,sam的区别)

cas.server-url-prefix=https://****/cas
cas.server-login-url=https://****/cas/login
cas.client-host-url=http://****:8080
cas.validation-type=CAS

启动配置注解

import net.unicon.cas.client.configuration.EnableCasClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableCasClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

如果要配置白名单,两种方式

第一种

需要放行的url,可以继承CasClientConfigurerAdapter类,重写configureAuthenticationFilter

import net.unicon.cas.client.configuration.EnableCasClient;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Configuration;

import java.util.Map;


@Configuration
public class CasClientConfig extends CasClientConfigurerAdapter {
@Override
public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) {
super.configureAuthenticationFilter(authenticationFilter);
Map<String, String> initParameters = authenticationFilter.getInitParameters();
// 配置地址,这里还可以配置很多,例如cas重定向策略等。
initParameters.put("ignorePattern", "/ignoreUrl1/");
}
}

同时启动的SpringBootApplication 也要配置cookie可用

import net.unicon.cas.client.configuration.EnableCasClient;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.transaction.annotation.EnableTransactionManagement;


@SpringBootApplication
@EnableTransactionManagement
@EnableCasClient
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Bean
public CookieSerializer httpSessionIdResolver() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setCookieName("JSESSIONID");
cookieSerializer.setUseHttpOnlyCookie(false);
cookieSerializer.setSameSite(null);
return cookieSerializer;
}
}

第二种方式

也可以配置需要拦截的url,不在配置内的就自动放行

cas.authentication-url-patterns=/need-filter-url/*,

前后端分离项目

接口接入cas-client,跨域问题及cookie携带需要配置

import net.unicon.cas.client.configuration.CasClientConfigurerAdapter;
import net.unicon.cas.client.configuration.EnableCasClient;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Map;


@Configuration
public class CasClientConfig extends CasClientConfigurerAdapter {
@Override
public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) {
super.configureAuthenticationFilter(authenticationFilter);
Map<String, String> initParameters = authenticationFilter.getInitParameters();
initParameters.put("authenticationRedirectStrategyClass",
"com.demo.filter.CustomAuthRedirectStrategy");
// 配置地址,这里还可以配置很多,例如cas重定向策略等。
initParameters.put("ignorePattern", "/ignoreUrl1/|/ignoreUrl2/|/ignoreUrl3/");
}

@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
//前端页面地址,可以配置多个
config.addAllowedOrigin("http://****:8081");
config.addAllowedOrigin("http://****:8081");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CorsFilter(source));
//需要拦截的url
registrationBean.addUrlPatterns("/url1/*");
registrationBean.addUrlPatterns("/url2/*");
registrationBean.addUrlPatterns("/url3/*");
registrationBean.setOrder(-2147483648);
return registrationBean;
}
}

CustomAuthRedirectStrategy

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jasig.cas.client.authentication.AuthenticationRedirectStrategy;


public class CustomAuthRedirectStrategy implements AuthenticationRedirectStrategy {

@Override
public void redirect(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String s) throws IOException {
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());

}

}

前端代码

http.js

import axios from 'axios'
import helper from './helper'
var qs = require('qs')
const ERR_CODE_LIST = { //常见错误码列表
[400]: "请求错误",
[401]: "登录失效或在其他地方已登录",
[403]: "拒绝访问",
[404]: "请求地址出错",
[408]: "请求超时",
[500]: "服务器内部错误",
[501]: "服务未实现",
[502]: "网关错误",
[503]: "服务不可用",
[504]: "网关超时",
[505]: "HTTP版本不受支持"
}
export function getErrMsg(error) {//通过error处理错误码
if(!error.response) {//无网络时单独处理
return {errCode:null, errMsg:"网络不可用,请刷新重试"}
}
const errCode = error.response.status //错误码
const errMsg = ERR_CODE_LIST[errCode] //错误消息

return {errCode: errCode,errMsg: errMsg ? `${errMsg} [${errCode}]` : error.message}
}
// axios.defaults.withCredentials=true;//让ajax携带cookie
// 引用axios,设置头文件
function apiAxios(method,rootUrl, url, params) {
return axios({
method: method,
url: url,
data:qs.stringify(params),
baseURL:rootUrl,
timeout: 600000,
dataType:"json",
async: true,
crossDomain: true,
withCredentials:true
}).catch(error => {
const {errCode,errMsg} = getErrMsg(error);
if(errCode == 401){
//登录失效 -> 跳转登录页
// http://****/cas/login 为cas-server的地址
// http://******/front/redirect 为后台用于处理跳转的地址
let hurl = 'http://****:8080/front/redirect';
window.location.href = 'http://****/cas/login?service=' + hurl;
}else{
showMsg(errMsg);
}
return Promise.reject(error)
})
}
export default {
get: function (rootUrl,url, params) {
return apiAxios('GET',rootUrl, url, params)
},
post: function (rootUrl,url, params) {
return apiAxios('post',rootUrl, url, params)
},
}

Api.js

import http from "./http";
let root = 'http://****:8080'
export default {
// 用户管理------------------
getUserInfo(data) {
return http.get( root, '/user/userInfo', data)
},
}

user.vue

async getUserInfo() {
let obj = {
param1: value1
};
let userInfo = await this.$api.getUserInfo(obj);
console.log(userInfo, '11');
},

跳转处理地址

@RequestMapping(value = "redirect", method = RequestMethod.GET)
public void redirect(HttpServletRequest request, HttpServletResponse httpServletResponse)
throws IOException {
//重定向到前端页面
httpServletResponse.sendRedirect("http://****:8081/#/demo.html");
}

文章目录
  1. 1. 背景
  2. 2. 普通的cas接入
    1. 2.0.1. 第一种
    2. 2.0.2. 第二种方式
  • 3. 前后端分离项目