由于现有系统外部接入须要,须要支持三方单点登陆。因为系统自己已是微服务架构,由多个业务独立的子系统组成,因此有本身的用户认证微服务(不是cas,咱们基础设施已经够多了,如今能不增长就不增长)。可是由于客户和其余接入(公有云网络)缘由,没法经过token+redis实现,因此还须要支持外部的cas。html
现有认证系统采用shiro实现,业务子系统采用shiro+token假登陆实现。如今要支持经过配置设置系统自身的认证子系统是否启用三方cas登陆。这样不管是使用本身的认证明现、仍是三方CAS,总体流程就彻底同样。java
从cas 4开始,官方就已经再也不提供release war,转而须要自行下载源码打包,网上不少,这里再也不阐述(下载依赖有点慢)。4.x以及以前的war能够从https://mvnrepository.com/artifact/org.jasig.cas/cas-server-webapp下载。下载后,解压到tomcat webapp目录:git
启动:github
修改下列配置:web
去除https认证:redis
在tomcat\webapps\cas\WEB-INF\deployerConfigContext.xml文件 的p:httpClient-ref="httpClient"后面添加p:requireSecure="false" 把tomcat\webapps\cas\WEB-INF\spring-configuration的 ticketGrantingTicketCookieGenerator.xml文件里面把p:cookieSecure="true"改成false; p:cookieMaxAge="-1"改成3600(-1是不保存cookie,3600秒是一个小时,保存登陆信息) 把tomcat\webapps\cas\WEB-INF\spring-configuration的 warnCookieGenerator.xml的p:cookieSecure="true"改成false p:cookieMaxAge="-1"改成3600
配置单点登出: spring
将tomcat\webapps\cas\WEB-INF\cas-servlet.xml中${cas.logout.followServiceRedirects:false}括号里的值改成trueapi
能够配置简单测试认证(去掉<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />注释)或基于数据源认证(能够参考https://www.cnblogs.com/wlwl/p/10056067.html)。tomcat
库依赖:服务器
<dependency> <groupId>net.unicon.cas</groupId> <artifactId>cas-client-autoconfig-support</artifactId> <version>1.5.0-GA</version> </dependency> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.2.1</version> <exclusions> <exclusion> <artifactId>servlet-api</artifactId> <groupId>javax.servlet</groupId> </exclusion> </exclusions> </dependency>
application.properties中加上下列配置:
cas.server-url-prefix=http://localhost:8080/cas
cas.server-login-url=http://localhost:8080/cas/login
cas.client-host-url=http://localhost:18080/
cas.use-session=true
cas.validation-type=cas
casClientLogoutUrl=http://localhost:8080/cas/logout?service=http://localhost:18080/tabase/logout.html
import net.unicon.cas.client.configuration.EnableCasClient; @EnableCasClient //启用cas client @CloudApplication public class ConsumerStarter { public static void main(String[] args) { CloudBootstrap.run(ConsumerStarter.class, args); } } package com.hs; import java.util.HashMap; import java.util.Map; import org.jasig.cas.client.authentication.AuthenticationFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CASAutoConfig { @Value("${cas.server-url-prefix}") private String serverUrlPrefix; @Value("${cas.server-login-url}") private String serverLoginUrl; @Value("${cas.client-host-url}") private String clientHostUrl; /** * 受权过滤器 * @return */ @Bean public FilterRegistrationBean filterAuthenticationRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new AuthenticationFilter()); // 设定匹配的路径 registration.addUrlPatterns("/*"); Map<String,String> initParameters = new HashMap<String, String>(); initParameters.put("casServerLoginUrl", serverUrlPrefix); initParameters.put("serverName", clientHostUrl); //忽略的url,"|"分隔多个url initParameters.put("ignorePattern", "/logout/success|/index"); registration.setInitParameters(initParameters); // 设定加载的顺序 registration.setOrder(1); return registration; } }
cas登陆成功后,会给应用返回ticket,其中包含了用户名。此时应用主页会被shiro过滤器UserFilter拦截,在其中能够判断HttpServletRequest上是否有ticket的用户名,有、且为有效用户名,就能够用该用户名模拟登陆了。这样整个shiro认证流程几乎没有变化,也不须要修改FormAuthenticationFilter。
cas在2.x和3.x版本获取用户名不同,这一点须要注意。
2.x版本client获取CAS传递过来的用户名的方法:
// 如下三者均可以 session.getAttribute(CASFilter.CAS_FILTER_USER); session.getAttribute("edu.yale.its.tp.cas.client.filter.user"); CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request); reqWrapper.getRemoteUser());
3.x版本client获取CAS传递过来的用户名的方法:
HttpServletRequest request = ServletActionContext.getRequest(); AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal(); String username = principal.getName(); Long orgnId = Long.parseLong(principal.getAttributes().get("orgnId").toString());
拿到后,就能够模拟登录了。
boolean isAllowed = super.isAccessAllowed(request, response, mappedValue); // if (isAllowed) { } else if (Boolean.valueOf(BaseConfig.getConfig("ta.casEnable","false"))) { AttributePrincipal principal = (AttributePrincipal)((HttpServletRequest) request).getUserPrincipal(); String username = principal.getName(); Subject subject = getSubject(request, response); AuthenticationToken token = new UsernamePasswordToken(); ((UsernamePasswordToken) token).setUsername(username); ((UsernamePasswordToken) token).setPassword(new char[] {'1'}); subject.login(token); isAllowed = super.isAccessAllowed(request, response, mappedValue); }
在shiro的logoutFilter中调用session.invalidate()便可。而后返回casClientLogoutUrl配置的地址退出cas便可。
/** * CAS添加开始 */ if (StringUtils.isNotBlank(BaseConfig.getConfig("casClientLogoutUrl"))) { ((HttpServletRequest)request).getSession().invalidate(); redirectUrl = BaseConfig.getConfig("casClientLogoutUrl"); } // CAS添加结束 return redirectUrl;
此致,就完整的实现了cas+shiro模拟登陆。
CAS ticket过时策略,参考:https://www.cnblogs.com/gao241/p/3367869.html
CAS流程介绍,参考:https://www.cnblogs.com/xiatian0721/p/8136305.html
CAS官方架构参考:https://apereo.github.io/cas/5.3.x/planning/Architecture.html