SpringBoot 前后端分离项目接入 CAS 单点登录,解决跨域方案

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

SpringBoot 前后端分离项目接入 CAS 单点登录,解决跨域方案

点击加入:
后端技术内卷群,一起学习!

背景

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

项目背景是公司有多套后台管理系统,都是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.classargs);
}

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

第一种

需要放行的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.classargs);
    }

    @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:nullerrMsg:"网络不可用,请刷新重试"}
  }
  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,
    timeout600000,
    dataType:"json",
    asynctrue,
    crossDomaintrue,
    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 {
  getfunction (rootUrl,url, params{
    return apiAxios('GET',rootUrl, url, params)
  },
  postfunction (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");
}

来源:https://blog.csdn.net/u013266867

精彩推荐
最全的java面试题库
开源版的高仿 “ 微信 ”,吊炸天!

与其在网上拼命找题? 不如马上关注我们~

SpringBoot 前后端分离项目接入 CAS 单点登录,解决跨域方案

↓点阅读原文,Java面试题库尽情看!

原文始发于微信公众号(Java面试题精选):SpringBoot 前后端分离项目接入 CAS 单点登录,解决跨域方案