一、准备工作
本地准备cas服务端(https://github.com/apereo/cas),下载cas代码并build成war包
本地准备Tomcat,并将打包好的cas包放至tomcat的webapps目录下,启动Tomcat
打开WEB-INF下application.properties文件,加入以下配置
#使用http协议
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
#退出登录后允许跳转
cas.logout.followServiceRedirects=true
#修改原本账号密码
cas.authn.accept.users=admin::123456
#修改端口号
server.port=8080
打开WEB-INF下services下HTTPSandIMAPS-10000001.json文件,修改以下配置。CAS默认使用的是https协议,我们修改让其支持http协议
启动Tomcat,测试是否部署成功
二、项目整合客户端
引入cas客户端依赖
<!--cas客户端-->
<dependency>
<groupId>com.cas</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.2.1</version>
</dependency>
编写cas拦截器的配置类(建议地址写在配置文件中)
package com.dibo.edu.cas;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionTrackingMode;
import java.util.*;
@Configuration
public class CasConfig {
@Bean
public ServletContextInitializer servletContextInitializer1() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE) );
}
};
}
/**
* 用于实现单点登出功能
*/
@Bean
public FilterRegistrationBean<SingleSignOutFilter> logOutFilter() {
FilterRegistrationBean<SingleSignOutFilter> authenticationFilter = new FilterRegistrationBean();
authenticationFilter.setFilter(new SingleSignOutFilter());
authenticationFilter.addUrlPatterns("/*");
authenticationFilter.setOrder(1);
return authenticationFilter;
}
//配置认证Filter
@Bean
public FilterRegistrationBean authenticationFilterRegistrationBean() {
FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
authenticationFilter.setFilter(new AuthenticationFilter());
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("casServerLoginUrl", "http://192.168.1.17:8080/cas/login");
initParameters.put("serverName", "http://192.168.1.17:8888");
//CAS过滤器白名单的设置,不同版本名称不同,可点进AuthenticationFilter进行查看
// initParameters.put("casWhiteUrl","/cas/casLogout");
authenticationFilter.setInitParameters(initParameters);
authenticationFilter.setOrder(2);
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add("/*");// 设置匹配的url
authenticationFilter.setUrlPatterns(urlPatterns);
return authenticationFilter;
}
//配置ticket验证Filter
@Bean
public FilterRegistrationBean ValidationFilterRegistrationBean(){
FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
authenticationFilter.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("casServerUrlPrefix", "http://192.168.1.17:8080/cas");
initParameters.put("serverName", "http://192.168.1.17:8888");
authenticationFilter.setInitParameters(initParameters);
authenticationFilter.setOrder(1);
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add("/*");// 设置匹配的url
authenticationFilter.setUrlPatterns(urlPatterns);
return authenticationFilter;
}
//配置获取用户信息的Filter
//request.getRemoteUser()
@Bean
public FilterRegistrationBean casHttpServletRequestWrapperFilter(){
FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());
authenticationFilter.setOrder(3);
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add("/*");// 设置匹配的url
authenticationFilter.setUrlPatterns(urlPatterns);
return authenticationFilter;
}
}
编写cas相关controller
@RequestMapping("/cas/casLogin")
public void casLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
String username = request.getRemoteUser();
log.info("登录用户账号:--------->{}", username);
String sessionId = request.getSession().getId();
log.info("sessionId:--------->{}", sessionId);
#撰写项目中逻辑(我这边是根据cas登录用户名获取系统登录token)
//获取token
String authToken = AuthServiceFactory.getAuthService(Cons.DICTCODE_AUTH_TYPE.PWD.name()).applyToken(credential);
log.info("token------->{}",authToken);
#重定向至前端页面(携带系统token)
// response.sendRedirect("http://210.28.145.114/ms/user/login?authToken=" + authToken);
response.sendRedirect("http://localhost:8000/user/login?authToken=" + authToken);
}
前端请求接口,你会发现浏览器地址栏发生变化,登录成功后cas会自动回调casLogin这个方法
输入账号密码登录出现跨域情况,重写cas包下过滤器(由于前端是ajax请求,这时候我们需要将接口进行标记返回至前端处理)
package com.dibo.edu.cas;//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.io.IOException;
import java.net.URLEncoder;
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;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;
public class AuthenticationFilter extends AbstractCasFilter {
private String casServerLoginUrl;
private boolean renew = false;
private boolean gateway = false;
private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
public AuthenticationFilter() {
}
protected void initInternal(FilterConfig filterConfig) throws ServletException {
if (!this.isIgnoreInitConfiguration()) {
super.initInternal(filterConfig);
this.setCasServerLoginUrl(this.getPropertyFromInitParams(filterConfig, "casServerLoginUrl", (String) null));
this.log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
this.setRenew(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "renew", "false")));
this.log.trace("Loaded renew parameter: " + this.renew);
this.setGateway(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "gateway", "false")));
this.log.trace("Loaded gateway parameter: " + this.gateway);
String gatewayStorageClass = this.getPropertyFromInitParams(filterConfig, "gatewayStorageClass", (String) null);
if (gatewayStorageClass != null) {
try {
this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
} catch (Exception var4) {
this.log.error(var4, var4);
throw new ServletException(var4);
}
}
}
}
public void init() {
super.init();
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
}
public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession(false);
Assertion assertion = session != null ? (Assertion) session.getAttribute("_const_cas_assertion_") : null;
if (assertion != null) {
filterChain.doFilter(request, response);
} else {
String serviceUrl = this.constructServiceUrl(request, response);
String ticket = CommonUtils.safeGetParameter(request, this.getArtifactParameterName());
boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
this.log.debug("no ticket and no assertion found");
String modifiedServiceUrl;
if (this.gateway) {
this.log.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}
if (this.log.isDebugEnabled()) {
this.log.debug("Constructed service url: " + modifiedServiceUrl);
}
String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
if (this.log.isDebugEnabled()) {
this.log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}
//判断请求方式是否为ajax请求
String header = request.getHeader("X-Requested-With");
if (header != null && "XMLHttpRequest".equals(header)) {
//给这个请求打上标记(登录已经超时或者认证未通过)
ajaxHttpToLogin(request, response, urlToRedirectTo);
return;
} else {
this.log.debug("redirecting to \"" + urlToRedirectTo + "\"");
response.sendRedirect(urlToRedirectTo);
}
// response.sendRedirect(urlToRedirectTo);
} else {
filterChain.doFilter(request, response);
}
}
}
public final void setRenew(boolean renew) {
this.renew = renew;
}
public final void setGateway(boolean gateway) {
this.gateway = gateway;
}
public final void setCasServerLoginUrl(String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}
public final void setGatewayStorage(GatewayResolver gatewayStorage) {
this.gatewayStorage = gatewayStorage;
}
/**
* ajax请求标记
*
* @param request
* @param response
* @param loginUrl
*/
private void ajaxHttpToLogin(HttpServletRequest request, HttpServletResponse response, String loginUrl) {
try {
response.setHeader("SESSIONSTATUS", "TIMEOUT");
response.setHeader("CONTEXTPATH", loginUrl);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);//403 禁止
} catch (Exception e) {
e.printStackTrace();
}
}
}
修改后登录,F12检查接口,处理后即可成功登录(跳转至系统中页面)
退出登录(先清除原系统的token,后注销session)
@GetMapping("/casLogout")
public JsonResult casLogout(HttpSession session, HttpServletResponse response) throws IOException {
//先执行源系统退出
IamSecurityUtils.logout();
//再执行Cas的单点退出功能
//注销session
session.invalidate();
// ids的退出地址 authserver为ids的上下文,logout为固定值
String casLogoutURL = "http://192.168.1.17:8080/cas/logout";
// service后面带的参数为应用的访问地址,需要使用URLEncoder进行编码
String redirectURL = casLogoutURL + "?service=http://192.168.1.17:8888/wxzy-rest/cas/casLogin";
response.sendRedirect(redirectURL);
return JsonResult.OK();
}
退出成功后(若不配置退出跳转将不会跳转登录页)
退出成功就会回调到casLogin方法,Cas过滤器发现没有登录,就又会跳转到Cas服务器的单点登录页面了
三、总结
关于springboot整合cas的内容分享结束了,具体项目集成成什么样还需具体分析。博主说的也不是很透彻,许多东西是在实践中慢慢摸索出来的。希望这边文章可以帮助到大家。