2019独角兽企业重金招聘Python工程师标准>>>

1.最近项目需要集成已经存在的cas系统。 但是目前已集成的系统都是jsp。而我们项目是前后端分离开发(伪),没有分开部署。

2.cas原理就不介绍了 网上例子很多。基本都是使用302重定向实现的。

下面介绍一下自己是怎么解决前后端分离的cas集成方式。

springmvc集成cas配置:

<security:http  create-session="always"  auto-config='false' entry-point-ref="casEntryPoint" use-expressions="true">
<security:intercept-url pattern="/index.html" access="hasRole('APP_USER')" /><security:intercept-url pattern="/redirect.html" access="hasRole('APP_USER')" />//因为是伪前后端分离 这2个url拦截是为了用户直接刷新页面时触发未登录情况跳转cas登录页使用。  index.html是我的主页<security:intercept-url pattern="/mvc/dispatch/**" access="hasRole('APP_USER')" /> --5.0不用带ROLE_开头<security:csrf disabled="true" /><security:custom-filter     position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" /><security:custom-filter ref="casFilter" position="CAS_FILTER" /><security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER" /><security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER" /><security:session-managementsession-authentication-strategy-ref="sas" /> </security:http><!-- <security:global-method-security pre-post-annotations="enabled" /> --><bean id="redirectSessionInformationExpiredStrategy"class="org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy"><constructor-arg name="invalidSessionUrl" value="/goLogin.html" /></bean><bean id="concurrencyFilter"class="org.springframework.security.web.session.ConcurrentSessionFilter"><constructor-arg name="sessionRegistry" ref="sessionRegistry" /><constructor-arg name="sessionInformationExpiredStrategy" ref="redirectSessionInformationExpiredStrategy" /></bean><bean id="sas"class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy"><constructor-arg><list><beanclass="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy"><constructor-arg ref="sessionRegistry" /><property name="maximumSessions" value="1" /></bean><!-- <beanclass="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"></bean> --><beanclass="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy"><constructor-arg ref="sessionRegistry" /></bean></list></constructor-arg></bean><bean id="sessionRegistry"class="org.springframework.session.security.SpringSessionBackedSessionRegistry"><constructor-arg ref="sessionRepository" /></bean><security:authentication-manager alias="authManager"><security:authentication-provider ref="casAuthProvider" /></security:authentication-manager><bean id="singleLogoutFilter" class="com.cas.XXSingleSignOutFilter" /><bean id="XXUrlLogoutSuccessHandler" class="com.cas.XXUrlLogoutSuccessHandler"><constructor-arg value="${cas.server.protocol}://${cas.server.host}/cas/logout?service=" /></bean>      <bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter" p:filterProcessesUrl="/j_spring_cas_security_logout"> 单点登出地址。 <constructor-arg ref="XXUrlLogoutSuccessHandler" /><constructor-arg><bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" /></constructor-arg></bean><bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties" p:sendRenew="false" p:service="${cas.service.protocol}://${cas.service.host}/${cas.service.web}/mvc/dispatch/login/cas" p:authenticateAllArtifacts="true" /> cas回调验证地址 5.0版本默认是拦截 /login/cas路径。如果要重写 需定义与filterProcessesUrl一致<bean id="casEntryPoint" class="com.cas.XXCasAuthenticationEntryPoint" p:serviceProperties-ref="serviceProperties" p:loginUrl="${cas.server.protocol}://${cas.server.host}/cas/login" /> cas登录地址<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter"p:authenticationManager-ref="authManager"p:serviceProperties-ref="serviceProperties"p:filterProcessesUrl="/mvc/dispatch/login/cas"><property name="authenticationDetailsSource"><bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"><constructor-arg ref="serviceProperties" /></bean></property><property name="authenticationFailureHandler"><bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler" p:defaultFailureUrl="/casfailed.html" /> cas回调验证失败地址</property> </bean><bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider" p:serviceProperties-ref="serviceProperties" p:key="an_id_for_this_auth_provider_only"><property name="authenticationUserDetailsService"><bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"><constructor-arg ref="casUserDetailsService" /></bean></property><property name="ticketValidator"><bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"><constructor-arg value="${cas.server.protocol}://${cas.server.host}/cas" /></bean></property></bean>

重点讲几个配置。由于前后端分离采用json交互,而cas是302重定向。 则我们需要改变cas入口点,不能使用默认的point。重写 CasAuthenticationEntryPoint为XXCasAuthenticationEntryPoint。

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.util.Assert;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;/*** 重写默认的实现,添加了向CAS注册服务时候参数带上sessionId,sessionId主要用于配合单点登出时候兼容集群模式*/
public class XXCasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {private static final Log logger = LogFactory.getLog(XXCasAuthenticationEntryPoint.class);private ServiceProperties serviceProperties;private String loginUrl;@Value("${cas.portal.url}")private String casPortalUrl;/*** @deprecated*/@Deprecatedprivate boolean encodeServiceUrlWithSessionId = true;public HisCasAuthenticationEntryPoint() {}public void afterPropertiesSet() throws Exception {Assert.hasLength(this.loginUrl, "loginUrl must be specified");Assert.notNull(this.serviceProperties, "serviceProperties must be specified");Assert.notNull(this.serviceProperties.getService(), "serviceProperties.getService() cannot be null.");}public final void commence(HttpServletRequest servletRequest, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {System.out.println(servletRequest.getRequestURI()+"-"+servletRequest.getRequestURL());HttpSession session=servletRequest.getSession();DefaultSavedRequest saveReq=(DefaultSavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");String urlEncodedService = this.createServiceUrl(servletRequest, response);String redirectUrl = this.createRedirectUrl(urlEncodedService);this.preCommence(servletRequest, response);System.out.println("hisCAsAUTH---------------------------------------------");if(servletRequest.getParameter("sso_ticket")!=null){//为电力医院统一平台认证添加票据号response.sendRedirect(redirectUrl + "&ticket=" + servletRequest.getParameter("sso_ticket"));}else{if("/index.html".equals(saveReq.getServletPath())){//如果是主页则跳转redirect.html获取token信息response.sendRedirect(saveReq.getRequestURL()+"redirect.html");}else if("/redirect.html".equals(saveReq.getServletPath())){ //如果当前是redirect且未登录则直接跳转cas登录页。这样cas登录成功后跳转到redirect.html会直接触发页面ajax请求获取到token,然后跳转主页response.sendRedirect(redirectUrl + "?" + servletRequest.getSession().getId());}else{//设置重定向url加上sessionId//response.sendRedirect(redirectUrl + "?" + servletRequest.getSession().getId());response.setContentType("application/json");  response.setStatus(200);  PrintWriter writer = response.getWriter();  //URLEncoder.encode(serviceUrl, "UTF-8")writer.write("{\"casError\":\"4111\",\"redirect\":\"" + redirectUrl + "?" + servletRequest.getSession().getId() + "\",\"portalUrl\":\""+casPortalUrl+ "?" + servletRequest.getSession().getId() +"\"}");  }}}protected String createServiceUrl(HttpServletRequest request, HttpServletResponse response) {return CommonUtils.constructServiceUrl((HttpServletRequest) null, response, this.serviceProperties.getService(), (String) null, this.serviceProperties.getArtifactParameter(), this.encodeServiceUrlWithSessionId);}protected String createRedirectUrl(String serviceUrl) {return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl, this.serviceProperties.isSendRenew(), false);}protected void preCommence(HttpServletRequest request, HttpServletResponse response) {}public final String getLoginUrl() {return this.loginUrl;}public final ServiceProperties getServiceProperties() {return this.serviceProperties;}public final void setLoginUrl(String loginUrl) {this.loginUrl = loginUrl;}public final void setServiceProperties(ServiceProperties serviceProperties) {this.serviceProperties = serviceProperties;}/*** @deprecated*/@Deprecatedpublic final void setEncodeServiceUrlWithSessionId(boolean encodeServiceUrlWithSessionId) {this.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId;}/*** @deprecated*/@Deprecatedprotected boolean getEncodeServiceUrlWithSessionId() {return this.encodeServiceUrlWithSessionId;}}

如上,如果触发cas跳转则改成返回json数据 。前端判断指定状态码,然后进行对应操作。具体 看下方前端介绍。 cas-client 3.5.0版本提供了这种接口。我这里使用的是 3.2.1版本。

requestSingleLogoutFilter是登出处理过滤器。主要是用来过滤客户端发起登出请求。XXUrlLogoutSuccessHandler 主要是用来处理登出后,再次登录跳转地址问题。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.util.StringUtils;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import java.io.IOException;
import java.net.URLEncoder;/*** Handles the navigation on logout by delegating to the* {@link org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler} base class logic.**/
public class HisUrlLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {@Value("${cas.portal.url}")private String casPortalUrl;private String casUrl;public HisUrlLogoutSuccessHandler(String casUrl) {this.casUrl = casUrl;}public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {String result = casUrl + URLEncoder.encode(casPortalUrl, "UTF-8");if (StringUtils.hasText(result)) {setDefaultTargetUrl(result);}super.handle(request, response, authentication);}}

XXSingleSignOutFilter主要用来处理单点登出。官方默认实现是 作废当前session。我集成了spring session。使用官方默认就可以。这里记录一下一种集群session删除思路。(redis)

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.jasig.cas.client.session.SingleSignOutHandler;
import org.jasig.cas.client.util.AbstractConfigurationFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;import com.asdc.jbp.hisLogin.util.CommonHelper;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;/*** 过滤器用于配置判断请求是否是退出系统请求,如果是退出系统请求将清空session缓存。*/
public class XXSingleSignOutFilter extends AbstractConfigurationFilter {private static final Log logger = LogFactory.getLog(HisSingleSignOutFilter.class);@Autowiredprivate RedisOperationsSessionRepository repository;@Value("${local.ip:000.000.000.000}")private String localIp;private static final SingleSignOutHandler handler = new SingleSignOutHandler();private AtomicBoolean handlerInitialized = new AtomicBoolean(false);public HisSingleSignOutFilter() {}public void init(FilterConfig filterConfig) throws ServletException {if (!this.isIgnoreInitConfiguration()) {handler.setArtifactParameterName(this.getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));handler.setLogoutParameterName(this.getPropertyFromInitParams(filterConfig, "logoutParameterName", "logoutRequest"));}handler.init();}public void setArtifactParameterName(String name) {handler.setArtifactParameterName(name);}public void setLogoutParameterName(String name) {handler.setLogoutParameterName(name);}public void setSessionMappingStorage(SessionMappingStorage storage) {handler.setSessionMappingStorage(storage);}public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;if (handler.isTokenRequest(request)) {handler.recordSession(request);} else {if (handler.isLogoutRequest(request)) {handler.destroySession(request);/*** 配合session共享机制,通过cas server记录的sessionId删除session在redis中的缓存,这一步骤主要用于业务集群后,在反向代理情况下,单点登出时候cas server登出请求随机请求.**/String logoutMessage = CommonUtils.safeGetParameter(request, "logoutRequest");String sessionId = XmlUtils.getTextForElement(logoutMessage, "SessionId");System.out.println("单点删除-----------------------------------");repository.delete(sessionId);logger.info("logout session id is:" + sessionId +";local is is :" + this.localIp  + ";cas server ip is:" + CommonHelper.analyzeClientIpAddress(request) + ";logout project is:" + ((HttpServletRequest) servletRequest).getContextPath());return;}this.log.trace("Ignoring URI " + request.getRequestURI());}filterChain.doFilter(servletRequest, servletResponse);}public void destroy() {}protected static SingleSignOutHandler getSingleSignOutHandler() {return handler;}}

cas配置基本如上。记录一下spring security 登录部分。


import java.util.Collection;
import java.util.LinkedList;import javax.annotation.Resource;import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service("casUserDetailsService")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Transactional(readOnly = false)
public class CasUserDetailsServiceImpl implements UserDetailsService {@Resourceprivate UserService userService;@SuppressWarnings("finally")@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {UserDetailImpl userDetail = new UserDetailImpl();try {System.out.println("查询用户信息--------------------------");User user = userService.queryUserByAccount(username);// 复制查询到的用户信息到实现类userDetail.setUserId(user.getUserId());userDetail.setPassword(user.getPasswd());userDetail.setUserName(username);Collection<GrantedAuthority> authorities = new LinkedList<GrantedAuthority>();/* List<GrantedAuthority> authorities = authorizationService.getAuthorities(token.getFunc());*/authorities.add(new SimpleGrantedAuthority("ROLE_APP_USER"));userDetail.setAuthorities(authorities);/*Token token = authorizationService.getUserTokenByUserId(user);Authentication auth = new PreAuthenticatedAuthenticationToken(token, token.getUser(), authorities);auth.setAuthenticated(true);SecurityContextHolder.getContext().setAuthentication(auth);*/} catch (ServiceException e) {e.printStackTrace();} finally {return userDetail;}}
}

根据cas返回的用户名查询 用户信息。 此处角色名称必须带上 ROLE_.

由于项目中获取用户信息时自己组装的 sessionDTO。而且是直接从httpsession中获取。故需要设置拦截器,注入用户信息。

servlet.xml中配置<mvc:interceptors><mvc:interceptor><mvc:mapping path="/dispatch/**" /><bean class="com.xx.interceptor.UserSessionInterceptor" /></mvc:interceptor></mvc:interceptors>import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;public class UserSessionInterceptor extends HandlerInterceptorAdapter {@Resourceprivate AuthorizationService authorizationService;@Resource(name = "")private UserService userService;@Autowiredprivate CompositeSessionAuthenticationStrategy sas;static Logger log=LoggerFactory.getLogger(UserSessionInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {HttpSession session = request.getSession();Object user = session.getAttribute("tUser");if(user==null){UserDetailImpl userDetail=(UserDetailImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();User originalUser =new User();originalUser.setUserCode(userDetail.getUsername());originalUser.setPasswd(userDetail.getPassword());try {originalUser = userService.queryTUserbyUserCodeAndPwd(originalUser);userService.queryAllDeptsAndAreas(originalUser);//根据userId构造session用户。Token token = authorizationService.getUserTokenByUserId(originalUser);long loginTime = System.currentTimeMillis();      session.setAttribute("tUser", token.getUser());session.setAttribute("loginUserCode", token.getUser().getUserCode());session.setAttribute("token", token);session.setAttribute("loginTime", loginTime);Authentication auth = new PreAuthenticatedAuthenticationToken(token.getUser().getAccount(), token.getUser(),null);auth.setAuthenticated(true);sas.onAuthentication(auth, request, response);} catch (Exception e) {log.error("session拦截器查询用户信息出错"+e);}finally{return true;}}return true;  }
}

这样就能保证 cas回调回来访问 controller能跟普通登录获取的session值一样。

重点来了!!前端如何处理

前面说了是返回json数据。那么前端必须拦截请求返回值,并且在返回之前 处理好cas单点登录。

我前端使用的是angularjs。故设置一个http请求拦截器即可。angular也有对应的拦截器。

var MetronicApp = angular.module("MetronicApp", [ "ui.router", "ui.bootstrap","pascalprecht.translate",// 国际化'mgcrea.ngStrap' // 弹框插件
]);
/**添加http拦截*/
MetronicApp.config([ '$httpProvider', function($httpProvider) {$httpProvider.interceptors.push('httpInterceptor');$httpProvider.defaults.headers.post = {'Content-Type' : 'application/x-www-form-urlencoded'}if (!$httpProvider.defaults.headers.get) {$httpProvider.defaults.headers.get = {};};$httpProvider.defaults.headers.common["X-Requeste-with"] = "XMLHttpRequest";$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';$httpProvider.defaults.headers.get['pragma'] = 'no-cache'
} ]);
MetronicApp.factory('httpInterceptor', [ '$q', '$injector', '$rootScope', '$window', '$timeout', function($q, $injector, $rootScope, $window, $timeout) {var httpInterceptor = {'responseError' : function(response) {var sign = 1;if (response.status == 404) {var rootScope = $injector.get('$rootScope');console.info(rootScope);var state = $injector.get('$rootScope').$state.current.name;console.info(state);rootScope.stateBeforLogin = state;rootScope.$state.go("home");return $q.reject(response);} else if (response.status == 417) {return $q.reject(response);} else if (response.status == 401) {$window.location.href = "./#/403.html";return $q.reject(response);} else if (response.status == 500) return $q.reject(response);};return $q.reject(response);},'response' : function(response) {if(response.status==200 && response.data.casError=='4111'){ //前端先判断当前是否登录,如果未登录获取后端url进行cas跳转判断。 4111是后端定义的返回码$.ajax({//几个参数需要注意一下type: "GET",//方法类型dataType: "html",//预期服务器返回的数据类型async: false, //同步是为了给当前业务请求返回数据。url: response.data.redirect+"&method=POST" ,//url 加上method=POST 可以让cas将ST以页面形式返回。 注意我这里将 cas服务端做了跨域处理。 必须设置授信证书。要不ajax有时候被浏览器拦截无法访问cas。xhrFields: {withCredentials: true // 这里设置了withCredentials  为了带上cookie},success: function (result) {console.log(result);//打印服务端返回的数据(调试用)if(result.indexOf("id=\"fm1\"")>0){ //判断是否cas登录页。如果是登录页则跳转登录页。alert("当前账号未登录,请先登录");window.location.href="./redirect.html";}//如果不是登录页我这里直接正则匹配了返回的请求回调地址。var reg=/action=\"(.*?)\"/g;console.log(result.match(reg));var url=result.match(reg)[0].replace(/action=\"/,"").replace(/\"/,"");console.log(url);var st_reg=/ST(.*?)\<\/textarea\>/g;var st_val=result.match(st_reg)[0].replace(/\<\/textarea\>/,"");console.log(st_val);
//拿到回调地址 拼接ticket 再次访问即可拿到 业务请求的返回值。$.ajax({//几个参数需要注意一下type: "POST",//方法类型dataType: "json",//预期服务器返回的数据类型async: false,url: url+"&ticket="+st_val ,//urlxhrFields: {withCredentials: true // 这里设置了withCredentials},success: function (result) {console.log(result);//打印服务端返回的数据(调试用)response.data=result; //将业务请求返回值返回。 response是当前业务请求的响应。},error : function(error) {alert("异常!");}});                    },error : function(error) {alert("异常!");}});}return response;},'request' : function(config) {//处理AJAX请求(否则后台IsAjaxRequest()始终false)config.headers['X-Requested-With'] = 'XMLHttpRequest';return config || $q.when(config);},'requestError' : function(config) {return $q.reject(config);}}return httpInterceptor;
} ]);
// 登录
MetronicApp.controller('redirectCtrl',function($scope, $http, $window, $q, $interval, $rootScope,$timeout) {var storage = window.sessionStorage;var storageLocal = window.localStorage;var loginParams = {};var loginData = mergeReauestData('LoginController','getToken',loginParams);var loginResult = sendPost($http,loginData, $q);loginResult.then(function(success) {var loginResult = JSON.parse(success);// 登录成功后,存储用户信息到storage的操作放到main.js中,页面初始化时var token = loginResult.token;var userinfo = loginResult.token.user;storage.setItem('token',JSON.stringify(token));storage.setItem('userinfo',JSON.stringify(userinfo));storageLocal.setItem('loginTime',loginResult.loginTime);// 登录的时候存放下setCookie("userId",userinfo.userId);$window.location.href = "./#/home.html";},function(error) {windowAlert(JSON.parse(error).errMsg);     });                 });// # sourceURL=RedirectController.js

因为我是测试可行性故我在前端主页前加了一层。正常应该在主页中配置如上方法。因为我的redirect.html被拦截了 所以跳转会触发302重定向至cas。然后cas登录后又会重定向至redirect.html 。最后页面ajax请求加载token,即实现了登录。

如果是前后端分开部署应该在ajax请求中再加一层去设置登录后的跳转主页。我的想法是:由于cas-client是将上一次访问页面都存储在了 session中 (SPRING_SECURITY_SAVED_REQUEST这么个key值中)。可以写一个接口让security不拦截 去设置主页 这样也可以实现。(DefaultSavedRequest没有set跳转路径的方法,不推荐重写)或者自己去重写AuthenticationSuccessHandler 实现自己的回调成功函数。默认是SavedRequestAwareAuthenticationSuccessHandler,拿到之前保存的路径重定向。

或者直接在后台定义一个重定一个重定向前端主页的接口。前端判断cas没有登录后访问这个接口。让该接口被拦截。然后再point中特殊判断是该接口请求的话直接重定向至cas。这样cas跳转回来该接口又能重定向至前端。大功告成

为了解决跨域问题。我对cas服务端做了跨域处理

跳转页如下:

<!DOCTYPE html>
<!--[if IE 8]> <html lang="en" class="ie8 no-js" data-ng-app="MetronicApp" > <![endif]-->
<!--[if IE 9]> <html lang="en" class="ie9 no-js" data-ng-app="MetronicApp"> <![endif]-->
<html lang="en" data-ng-app="MetronicApp">
<head>
<meta charset="utf-8" />
<title>正在跳转</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1" name="viewport" />
<meta content="" name="description" />
<meta content="" name="author" /><link rel="shortcut icon" href="favicon.ico" />
</head>
<body class="login" >
<div ng-controller="redirectCtrl"></div><script src="resources/global/plugins/respond.min.js"></script><script src="resources/global/plugins/excanvas.min.js"></script><script src="resources/global/plugins/jquery.min.js"></script><script src="resources/global/plugins/bootstrap/js/bootstrap.min.js"></script><scriptsrc="resources/global/plugins/bootstrap-hover-dropdown/bootstrap-hover-dropdown.min.js"></script><script src="resources/global/plugins/angularjs/angular.min.js"></script><scriptsrc="resources/global/plugins/angularjs/angular-sanitize.min.js"></script><script src="resources/global/plugins/angularjs/angular-touch.min.js"></script><scriptsrc="resources/global/plugins/angularjs/plugins/angular-ui-router.min.js"></script><scriptsrc="resources/global/plugins/angularjs/plugins/ocLazyLoad.min.js"></script><scriptsrc="resources/global/plugins/angularjs/plugins/ui-bootstrap-tpls.min.js"></script><script src="packages/index/js/common/CommonService.js"></script><script src="packages/index/js/main.js"></script><script src="packages/index/js/directives.js"></script><script src="resources/global/scripts/app.min.js"></script><script src="resources/layouts/layout/scripts/layout.min.js"></script><script src="resources/layouts/global/scripts/quick-sidebar.min.js"></script><script src="packages/pages/js/controllers/RedirectController.js"></script><script src="resources/global/plugins/angular-translate.min.js"></script><scriptsrc="resources/global/plugins/angular-translate-loader-static-files.min.js"></script><script src="packages/index/js/common/json2.js"></script><script src="packages/index/js/common/MultiLanguage.js"></script><scriptsrc="resources/global/plugins/angularjs/plugins/angular-strap/angular-strap.js"></script><scriptsrc="resources/global/plugins/angularjs/plugins/angular-strap/angular-strap.tpl.js"></script>
</body>
</html>

cas跨域处理:加个过滤器

package org.jasig.cas.web;import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class cascorfFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// TODO Auto-generated method stub}@Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {HttpServletResponse response1 = (HttpServletResponse) response; HttpServletRequest request1=(HttpServletRequest)request;response1.setHeader("Access-Control-Allow-Origin", request1.getHeader("Origin"));  response1.setHeader("Access-Control-Allow-Credentials", "true");response1.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");  response1.setHeader("Access-Control-Max-Age", "3600");  response1.setHeader("Access-Control-Allow-Headers","Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,token");  chain.doFilter(request, response);  }@Overridepublic void destroy() {// TODO Auto-generated method stub}}

感谢https://gogo1217.iteye.com/blog/2425080提供的思路。虽然代码不全 但是提供了 method=POST的方法思路

总结: cas服务端是3.X。 method=POST、HEADER、GET 官网上说是5.x提供的方法。但是3.X也能通过ajax获取,故没有升级cas。3.x不支持HEADER

cas5.x 设置了POST 返回的是ST跳转页面。浏览器是看不出来的 建议自己用postman等调试。 设置HEADER时确实在头信息里返回了 ST信息等。但是我调试时疯狂重定向都懵逼了。 cas5.x官网说是在responseType中设置响应类型。可以在配置文件中指定类型。

下次介绍一下spring boot+spring security+cas的集成方式。

转载于:https://my.oschina.net/u/3065626/blog/3042486

springmvc集成cas,并解决前后端分离情况相关推荐

  1. Vue整合nginx:(1)开发环境npm run dev下,通过nginx解决前后端分离造成的跨域问题

    Vue整合nginx:(1)开发环境npm run dev下,通过nginx解决前后端分离造成的跨域问题 参考文章: (1)Vue整合nginx:(1)开发环境npm run dev下,通过nginx ...

  2. Nginx完美解决前后端分离端口号不同导致的跨域问题

    Nginx完美解决前后端分离端口号不同导致的跨域问题 参考文章: (1)Nginx完美解决前后端分离端口号不同导致的跨域问题 (2)https://www.cnblogs.com/PyKK2019/p ...

  3. JWT(解决前后端分离和微服务的用户会话跟踪问题)

    这里写目录标题 JWT:解决前后端分离和微服务的用户会话跟踪问题 与传统sessio验证的区别: 基于 token 的鉴权机制 JWT的主要引用场景及优点 JWT的构成: JWT搭建 案例: JWT: ...

  4. localStorage.setItem()前后端分离情况下使用

    localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除. localStorage 属性是只读的. 前后端分离情况下,可以将后端返回的数据保存到localStor ...

  5. springBoot 解决前后端分离项目中跨越请求,同源策略

    今天在做项目的过程,采用前后端分离技术的时遇到采用ajax请求无法访问后台接口,按F12,查看浏览器运行状态时,报如下错误 为了解决浏览的同源策略,就必须了解什么是同源策略. 1.什么是同源策略 同源 ...

  6. django集成vue,实现前后端分离开发

    目前,前后端分离的开发方式越发常见,已成为主要开发方式,那么django作为一个python快速开发中小型web端最常见的框架,django虽然也有自己前端模板,但是毕竟不是很好,而且写在一起,也不符 ...

  7. Nginx解决前后端分离(适用于传统项目改前后端分离)

    最近写项目碰到个前后端分离的问题,踩了不少坑,给大家分享一些经验. 项目是传统的项目,没有做前后端分离,现在根据业务现状,需要改成前后端分离的形式去做开发,上来就碰到个头大的事,cookie没办法在前 ...

  8. Java前后端分离第三方登录_网站前后端分离情况下如何实现QQ微信等第三方登陆-Fun言...

    前言 最近在弄第三方登录,实现起来很简单,就是调几个接口获取个人信息即可,详细了解:maven项目整合QQauth2.0第三方登录详细说明,然后因为本网站是前后端分离的,所以不能再回调接口那里直接跳转 ...

  9. 网站前后端分离情况下如何实现QQ微信等第三方登陆

    本文转自Fun言网:https://funyan.cn/p/1705.html 前言 最近在弄第三方登录,实现起来很简单,就是调几个接口获取个人信息即可,详细了解:maven项目整合QQauth2.0 ...

最新文章

  1. c# out ref
  2. 【一篇文章搞懂】25K大牛甩出的超详细面试总结
  3. java list addall源码_Java集合:ArrayList源码分析
  4. 网络安全技术文章征稿启事
  5. CV Code | 本周新出计算机视觉开源代码汇总(语义分割、目标检测、超分辨率、网络结构设计、训练策略等)...
  6. Linux系统NFS故障现象
  7. Python 语言 Hello world
  8. linux php 集成安装,1.2.2 XAMPP-Linux版PHP集成化安装包
  9. 简单家乡风景静态HTML网页设计作品 DIV布局家乡介绍网页模板代码
  10. tableViewCell、collectionViewCell、组头组尾等总结
  11. SpringBoot(三):最简版登录拦截案例
  12. java背单词_java实现背单词程序
  13. 朝鲜打造尖端技术开发区 欲将开城变“硅谷”
  14. 数据恢复中的“老大难”:文件碎片 | 专家约稿
  15. 抗混叠滤波器 - ADC前端放大器和RC滤波器设计实现步骤
  16. (150)FPGA时序违例超详细总结
  17. YDOOK:ANSYS 进行电磁场仿真的第四步:后处理 查看计算结果
  18. Q for Mortals2笔记 -- 概述
  19. 揭秘职场:不能跟同事诉说的话题
  20. dayjs 获取上个月的今天

热门文章

  1. webix icon 图标
  2. asp毕业设计——基于vb+VB.NET+SQL Server的公文管理系统设计与实现(毕业论文+程序源码)——公文管理系统
  3. 计算机二级ms难度,为什么二级ms通过低
  4. t3实训控制系统服务器,T3教育专版,实训控制系统,考试服务器安装,IIS已经装过了,但是还是提示这个错误。电脑是SERVER2003,ISO原版!...
  5. springboot银行客户管理系统毕业设计源码250903
  6. 手机号号段,正则,校验
  7. hp笔记本的计算机在哪里,惠普电脑型号在哪里看
  8. 设计一个简单的购物页面(html+css+php)
  9. vue 实现分页和多页签功能
  10. 解决多线程并发安全问题