CAS单点登录源码解析之【客户端】
cas 3.5.3服务器搭建+spring boot集成+shiro模拟登录(不修改现有shiro认证架构)。因为我们属于供应商,所以有些客户那里会需要接对方的CAS,所以没有使用shiro和cas的直接集成模式,如果是这种情况,可以参考:https://blog.csdn.net/catoop/article/details/50534006。
Cas Client主要有四个核心过滤器:
l AuthenticationFilter
l TicketValidationFilter
l HttpServletRequestWrapperFilter
l AssertionThreadLocalFilter
他们的作用解释如下:
AuthenticationFilter
AuthenticationFilter用来拦截所有的请求,用以判断用户是否需要通过Cas Server进行认证,如果需要则将跳转到Cas Server的登录页面。如果不需要进行登录认证,则请求会继续往下执行。
AuthenticationFilter有两个用户必须指定的参数,一个是用来指定Cas Server登录地址的casServerLoginUrl,另一个是用来指定认证成功后需要跳转地址的serverName或service。service和serverName只需要指定一个就可以了。当两者都指定了,参数service将具有更高的优先级,即将以service指定的参数值为准。service和serverName的区别在于service指定的是一个确定的URL,认证成功后就会确切的跳转到service指定的URL;而serverName则是用来指定主机名,其格式为{protocol}:{hostName}:{port},如:https://localhost:8443,当指定的是serverName时,AuthenticationFilter将会把它附加上当前请求的URI,以及对应的查询参数来构造一个确定的URL,如指定serverName为“http://localhost”,而当前请求的URI为“/app”,查询参数为“a=b&b=c”,则对应认证成功后的跳转地址将为“http://localhost/app?a=b&b=c”。
除了上述必须指定的参数外,AuthenticationFilter还可以指定如下可选参数:
l renew:当指定renew为true时,在请Cas Server时将带上参数“renew=true”,默认为false。
l gateway:指定gateway为true时,在请求Cas Server时将带上参数“gateway=true”,默认为false。
l artifactParameterName:指定ticket对应的请求参数名称,默认为ticket。
l serviceParameterName:指定service对应的请求参数名称,默认为service。
TicketValidationFilter
在请求通过AuthenticationFilter的认证之后,如果请求中携带了参数ticket则将会由TicketValidationFilter来对携带的ticket进行校验。TicketValidationFilter只是对验证ticket的这一类Filter的统称,其并不对应Cas Client中的一个具体类型。Cas Client中有多种验证ticket的Filter,都继承自AbstractTicketValidationFilter,它们的验证逻辑都是一致的,都有AbstractTicketValidationFilter实现,所不同的是使用的TicketValidator不一样。默认是Cas10TicketValidationFilter。
可选参数包括:
l redirectAfterValidation :表示是否验证通过后重新跳转到该URL,但是不带参数ticket,默认为true。
l useSession :在验证ticket成功后会生成一个Assertion对象,如果useSession为true,则会将该对象存放到Session中。如果为false,则要求每次请求都需要携带ticket进行验证,显然useSession为false跟redirectAfterValidation为true是冲突的。默认为true。
l exceptionOnValidationFailure :表示ticket验证失败后是否需要抛出异常,默认为true。
l renew:当值为true时将发送“renew=true”到Cas Server,默认为false。
HttpServletRequestWrapperFilter
HttpServletRequestWrapperFilter用于将每一个请求对应的HttpServletRequest封装为其内部定义的CasHttpServletRequestWrapper,该封装类将利用之前保存在Session或request中的Assertion对象重写HttpServletRequest的getUserPrincipal()、getRemoteUser()和isUserInRole()方法。这样在我们的应用中就可以非常方便的从HttpServletRequest中获取到用户的相关信息。
AssertionThreadLocalFilter
AssertionThreadLocalFilter是为了方便用户在应用的其它地方获取Assertion对象,其会将当前的Assertion对象存放到当前的线程变量中,那么以后用户在程序的任何地方都可以从线程变量中获取当前Assertion,无需再从Session或request中进行解析。该线程变量是由AssertionHolder持有的,我们在获取当前的Assertion时也只需要通过AssertionHolder的getAssertion()方法获取即可,如:
Assertion assertion = AssertionHolder.getAssertion();
cas client/shiro过滤器顺序,集成cas的情况下,cas客户端总是第一个过滤器。
org.jasig.cas.client.validation.AbstractTicketValidationFilter#doFilter
org.jasig.cas.client.util.CommonUtils#safeGetParameter
org.jasig.cas.client.authentication.AuthenticationFilter#doFilter --在这里修改源码,拿到assertion、且为空后需要判断token是否存在且有效,如果存在且有效,说明已经登录过,则直接返回
org.jasig.cas.client.util.CommonUtils#constructRedirectUrl
javax.servlet.http.HttpServletResponse#sendRedirect --此时请求去了CAS
最后进入shiro过滤器UserFilter.isAccessAllowed
因为在userfilter里面标准的cas登录后,是可以通过UserPrincipal拿到当前的用户信息的,但是当我们是集群模式的时候因为直接在AuthenticationFilter#doFilter中拦截返回了,且没有明确设置,自然就拿不到了,servletrequest中没有提供设置UserPrincipal的入口,cas的org.jasig.cas.client.validation.Cas20ServiceTicketValidator#parseResponseFromServer是创建了,但是org.jasig.cas.client.validation.Cas20ServiceTicketValidator#customParseResponse的实现体是空的。如下:
protected final Assertion parseResponseFromServer(final String response) throws TicketValidationException {
final String error = XmlUtils.getTextForElement(response,
"authenticationFailure");
if (CommonUtils.isNotBlank(error)) {
throw new TicketValidationException(error);
}
final String principal = XmlUtils.getTextForElement(response, "user");
final String proxyGrantingTicketIou = XmlUtils.getTextForElement(response, "proxyGrantingTicket");
final String proxyGrantingTicket = this.proxyGrantingTicketStorage != null ? this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou) : null;
if (CommonUtils.isEmpty(principal)) {
throw new TicketValidationException("No principal was found in the response from the CAS server.");
}
final Assertion assertion;
final Map<String,Object> attributes = extractCustomAttributes(response);
if (CommonUtils.isNotBlank(proxyGrantingTicket)) {
final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes, proxyGrantingTicket, this.proxyRetriever);
assertion = new AssertionImpl(attributePrincipal);
} else {
assertion = new AssertionImpl(new AttributePrincipalImpl(principal, attributes));
}
customParseResponse(response, assertion);
return assertion;
}
stackoverflow也没搜到。如下:
https://stackoverflow.com/questions/25885747/when-and-where-java-security-set-userprincipal
https://docs.oracle.com/javase/7/docs/jre/api/security/jaas/spec/com/sun/security/auth/UserPrincipal.html
此时如果设置UserPrincipal需要的话,需要自己通过HttpServletRequestWrapperFilter重写一遍,如下:
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final AttributePrincipal principal = retrievePrincipalFromSessionOrRequest(servletRequest);
filterChain.doFilter(new CasHttpServletRequestWrapper((HttpServletRequest) servletRequest, principal), servletResponse);
}
protected AttributePrincipal retrievePrincipalFromSessionOrRequest(final ServletRequest servletRequest) {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpSession session = request.getSession(false);
final Assertion assertion = (Assertion) (session == null ? request.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION));
return assertion == null ? null : assertion.getPrincipal();
}
这样的话,shiro UserFilter里面就可以通过
AttributePrincipal principal = (AttributePrincipal)((HttpServletRequest) request).getUserPrincipal();
拿到登录用户的信息了。
CAS单点登录源码解析之【客户端】的更多相关文章
- Fabric1.4源码解析:客户端安装链码
看了看客户端安装链码的部分,感觉还是比较简单的,所以在这里记录一下. 还是先给出安装链码所使用的命令好了,这里就使用官方的安装链码的一个例子: #-n 指定mycc是由用户定义 ...
- Spring Cloud系列(四):Eureka源码解析之客户端
一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-netflix-eureka-client.jar的 ...
- Fabric1.4源码解析:客户端创建通道过程
在使用Fabric创建通道的时候,通常我们执行一条命令完成,这篇文章就解析一下执行这条命令后Fabric源码中执行的流程. peer channel create -o orderer.example ...
- HDFS源码解析:教你用HDFS客户端写数据
摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...
- Fabric1.4源码解析:链码实例化过程
之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程.其实这几个过程内容已经很相似了,都是涉及到Proposal,不过整体流程还是要说一下的. 同样,切入点仍然是fabr ...
- cas客户端流程详解(源码解析)--单点登录
博主之前一直使用了cas客户端进行用户的单点登录操作,决定进行源码分析来看cas的整个流程,以便以后出现了问题还不知道是什么原因导致的 cas主要的形式就是通过过滤器的形式来实现的,来,贴上示例配置: ...
- FileZilla客户端源码解析
FileZilla客户端源码解析 FTP是TCP/IP协议组的协议,有指令通路和数据通路两条通道.一般来说,FTP标准命令TCP端口号是21,Port方式数据传输端口是20. FileZilla作为p ...
- Netty5客户端源码解析
Netty5客户端源码解析 今天来分析下netty5的客户端源码,示例代码如下: import io.netty.bootstrap.Bootstrap; import io.netty.channe ...
- Netty源码解析—客户端启动
Netty源码解析-客户端启动 Bootstrap示例 public final class EchoClient { static final boolean SSL = System.getPro ...
随机推荐
- 5.2 odex文件
odex是OptimizedDEX的缩写,是优化过的dex文件 odex两种存在方式: 1. 从apk程序中提取,和apk文件放在一起,后缀 odex,此类文件多是AndroidRom系统文件 2. ...
- oracle 排序后分页查询
demo: select * from ( select * from DEV_REG_CFG_CAMERA where 1 = 1 order by unid asc) where rownum & ...
- ML-线性模型 泛化优化 之 L1 L2 正则化
认识 L1, L2 从效果上来看, 正则化通过, 对ML的算法的任意修改, 达到减少泛化错误, 但不减少训练误差的方式的统称 训练误差 这个就损失函数什么的, 很好理解. 泛化错误 假设 我们知道 预 ...
- mybatis + oracle,出现ORA-01461:仅能绑定要插入LONG列的LONG值
1.这个异常是指,用户向数据库执行插入数据操作时,某条数据的某个字段值过长,如果是varchar2类型的,当长度超过2000,--4000(最大值)之间的时候,oracle会自动将该字段值转为long ...
- Java开发分析工具JProfiler的详细使用方法解析
JProfiler 11 是一款功能强大的Java代码分析工具,JProfiler的直观UI可帮助您解决性能瓶颈,确定内存泄漏并了解线程问题且JProfiler 11 Mac破解版配置会话非常简单,第 ...
- 如何监控网站URL是否正常?
监控网站URL是否正常最常见的方法莫过于wget和curl命令了,这两个命令都是非常强大的,强大到网友查看帮助都无从选择的地步了,今天呢,老男孩老师正好录制Shell编程实战课程,因此,顺便总结了一下 ...
- 开发基础之牛逼哄哄的 Lambda 表达式,简洁优雅就是生产力
什么是Lambda? 我们知道,对于一个Java变量,我们可以赋给其一个“值”. 如果你想把“一块代码”赋给一个Java变量,应该怎么做呢? 比如,我想把右边那块代码,赋给一个叫做aBlockOfCo ...
- springMVC中的日期格式的转化
一.jsp页面传递到controller的日期 如果实体类中封装的日期类型为Date,而jsp页面中的传来的为string类型,这个时候后台就会报错,出现400错误,原因是前后端的数据类型不一致.要将 ...
- moya
https://juejin.im/post/5ac2cf34f265da23a1421483 https://juejin.im/post/5a69e9f9f265da3e290c6782
- 27.SpringBoot和SpringMVC的区别
所以,用最简练的语言概括就是: Spring 是一个“引擎”: Spring MVC 是基于Spring的一个 MVC 框架: Spring Boot 是基于Spring4的条件注册的一套快速开发整合 ...