单点登录(十六)-----遇到问题-----cas4.2.x登录成功后报错No principal was found---cas中文乱码问题完美解决
情况
我们之前已经完成了cas4.2.x登录使用mongodb验证方式并且自定义了加密。
单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密
但是悲剧的是 当用户名是中文名时或者获取的其他属性中有中文名时成功登录后报错No principal was found。
javax.servlet.ServletException: org.jasig.cas.client.validation.TicketValidationException: No principal was found in the response from the CAS server.
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:155)
org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:102)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:110)
root cause
org.jasig.cas.client.validation.TicketValidationException: No principal was found in the response from the CAS server.
org.jasig.cas.client.validation.Cas20ServiceTicketValidator.parseResponseFromServer(Cas20ServiceTicketValidator.java:82)
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:188)
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:132)
org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:102)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:110)
原因
我这里使用的是cas server 4.2.7 ,cas client的版本通过maven引入3.4.1。
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.4.1</version>
</dependency>
我们在console中查看报错原因有
ervlet.service() for servlet jsp threw exception
org.xml.sax.SAXParseException: The element type "cas:user" must be terminated by the matching end-tag "</cas:user>"
说明user的返回值中,中文变成了乱码导致异常抛出。
我们来分析下登录的流程。
登录认证成功后会认证ticket然后跳转到cas client。
认证ticket和承载转发到cas client的信息是由casServiceValidationSuccess.jsp页面来完成的。但是cas-server-webapp中是有2.0和3.0版本的协议的。
我们在cas client中定义了使用cas的哪种协议。
cas2.0协议的验证结果页面是通过目录:cas/WEB-INF/view/jsp/protocol/2.0/ 下定义的jsp(jstlview)模板页面来定义的。
casServiceValidationSuccess.jsp:对应验证成功的页面(这个页面还包含用户登录的帐户名)。
casServiceValidationFailure.jsp:对应验证失败页面。
所以问题就出在casServiceValidationSuccess.jsp页面的编码以及cas client这边接受这个页面编码的问题上。
cas client这边接受这个页面编码是在cas-client-core-3.4.1的项目里Cas20ProxyReceivingTicketValidationFilter.java中完成的。
只要casServiceValidationSuccess.jsp页面的编码和Cas20ProxyReceivingTicketValidationFilter.java使用的编码一致就不会出现接受信息的乱码问题。
我们可以看到cas server 4.2.7的jsp页面是使用 utf-8编码的。
在页面的头部有:
<%@ page session="false" contentType="application/xml; charset=utf-8" %>
但是cas-client-core-3.4.1的项目里Cas20ProxyReceivingTicketValidationFilter.java中,getTicketValidator方法里设置的默认编码方式是null。
根据测试当默认编码方式是null时只能识别到GBK模式或者gb2312的编码。
解决方案
知道了方案之后我们就可以开始解决了。之前查了很多网上的说法,都只说到了一些某一种方式,而且不是全都适用。
这里我们知道了 乱码的原因之后 就可以从根本上解决乱码问题。
也就是需要让 casServiceValidationSuccess.jsp页面的编码和Cas20ProxyReceivingTicketValidationFilter.java使用的编码一致就不会出现接受信息的乱码问题。
解决方案一casServiceValidationSuccess.jsp使用gbk编码
最简单的一种修改方法,将 casServiceValidationSuccess.jsp页面的编码修改为gbk或者gb2312。
因为我们之前已经说过 Cas20ProxyReceivingTicketValidationFilter.java的编码模式默认是null时可以识别到gbk和gb2312的编码的。
所以我们只需要把 casServiceValidationSuccess.jsp页面的
<%@ page session="false" contentType="application/xml; charset=utf-8" %>
修改成
<%@ page session="false" contentType="application/xml; charset=gbk" %>
或者
<%@ page session="false" contentType="application/xml; charset=gb2312" %>
即可。
解决方案二修改源码两个文件都使用utf-8编码
有些同学会说了,我们的cas server项目和cas client项目都使用的utf-8,为什么casServiceValidationSuccess.jsp要使用gbk编码,统一改成utf-8行不行。
答案是可以的。
那我们就需要同时修改两个文件了。
首先casServiceValidationSuccess.jsp使用utf-8编码。也就是头部保持:
<%@ page session="false" contentType="application/xml; charset=utf-8" %>
然后
如果我们的cas-client-core-3.4.1是子项目的形式那么我们就可以直接修改Cas20ProxyReceivingTicketValidationFilter.java文件中的getTicketValidator方法里设置的默认编码方式是utf-8。
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix,"utf-8", factory));
validator.setEncoding("utf-8");
解决方案三配置法两个文件都使用utf-8编码
有些同学会说 我们的cas-client-core-3.4.1用的不是项目引入形式,而是一个jar包,不能直接修改源码怎么办,其实可以在配置文件上修改。(这种方式不生效请看方案四,有讲原因)
我们在之前的ConfigurationKeys中就看到encoding是参数配置的形式可以引入的。
那么我们还是修改两个文件一个是casServiceValidationSuccess.jsp另一个是cas client的web.xml中配置
首先casServiceValidationSuccess.jsp使用utf-8编码。也就是头部保持:
<%@ page session="false" contentType="application/xml; charset=utf-8" %>
然后
web.xml中找到
<!-- 该过滤器负责对Ticket的校验工作,必须-->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://127.0.0.1:8080/cas/</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:8080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<!-- 对项目中的哪些路径做登录拦截-->
<url-pattern>/*</url-pattern>
</filter-mapping>
需要加入encoding参数配置
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
修改后的配置为:
<!-- 该过滤器负责对Ticket的校验工作,必须-->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://127.0.0.1:8080/cas/</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:8080</param-value>
</init-param>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<!-- 对项目中的哪些路径做登录拦截-->
<url-pattern>/*</url-pattern>
</filter-mapping>
解决方案四使用URLEncoder来暴力相互转码
有些同学说怎么用配置法设置之后不生效,还是乱码? 我也遇到了。跟进源代码里发现 这一个cas client中使用的cas-client-core的版本是3.1.10。
cas-client-core-3.1.10里竟然没有用到配置参数encoding。。。。
所以配置法在低版本的cas-client-core中是无效的。
这种情况的话 只能下载cas-client-core3.1.10源码直接修改代码。
如果是jar包引用的话 还有另一种比较变态的改法,就是使用URLEncoder来暴力相互转码。
首先casServiceValidationSuccess.jsp使用utf-8编码。也就是头部修改为(多加import="java.net.URLEncoder"):
<%@ page session="false" contentType="application/xml; charset=utf-8" import="java.net.URLEncoder" %>
然后
casServiceValidationSuccess.jsp页面里的
<cas:user>${fn:escapeXml(principal.id)}</cas:user>
修改成
<c:set var="val" value="${fn:escapeXml(principal.id)}"/>
<cas:user><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:user>
即可。
这里处理的只是登录名属性。
如果是其他属性的话 需要新增
<cas:attributes>
<c:forEach items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"var="attr">
<c:set var="val" value="${attr.value}"/>
<cas:${attr.key}><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:${attr.key}>
</c:forEach>
</cas:attributes>
则获取多属性的casServiceValidationSuccess.jsp最终配置为
<%@ page session="false" contentType="application/xml; charset=utf-8" import="java.net.URLEncoder" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<c:set var="val" value="${fn:escapeXml(principal.id)}"/>
<cas:user><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:user>
<c:if test="${not empty pgtIou}">
<cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
</c:if>
<c:if test="${fn:length(chainedAuthentications) > 0}">
<cas:proxies>
<c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(chainedAuthentications)}" step="1">
<cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
</c:forEach>
</cas:proxies>
</c:if>
<cas:attributes>
<c:forEach items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"var="attr">
<c:set var="val" value="${attr.value}"/>
<cas:${attr.key}><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:${attr.key}>
</c:forEach>
</cas:attributes>
</cas:authenticationSuccess>
</cas:serviceResponse>
只获取id的casServiceValidationSuccess.jsp最终配置为
<%@ page session="false" contentType="application/xml; charset=utf-8" import="java.net.URLEncoder" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<c:set var="val" value="${fn:escapeXml(principal.id)}"/>
<cas:user><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:user>
<c:if test="${not empty pgtIou}">
<cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
</c:if>
<c:if test="${fn:length(chainedAuthentications) > 0}">
<cas:proxies>
<c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(chainedAuthentications)}" step="1">
<cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
</c:forEach>
</cas:proxies>
</c:if>
</cas:authenticationSuccess>
</cas:serviceResponse>
而且如果是转码设置则客户端取值的时候也需要转码,所以说有点变态,还是建议前三种解决方案。
cas client在jsp中获取
<%
Assertion assertion = AssertionHolder.getAssertion();
AttributePrincipal ap = assertion.getPrincipal();
String id = ap.getName();
Map<String,Object> att = ap.getAttributes();
out.print("<br/>"+id);
out.print("<br/>"+att);
String name = URLDecoder.decode(""+att.get("username"), "UTF-8");
out.println("<br/>"+name);
%>
cas client在java中获取
HttpServletRequest request = ServletActionContext.getRequest();
/*获取单点登录服务器传递过来的用户信息*/
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
if (principal != null) {
Map<String, Object> attributes = principal.getAttributes();
if (attributes.size() > 0) {
URLDecoder.decode(""+attributes.get("userid"), "UTF-8");
URLDecoder.decode(""+attributes.get("username"), "UTF-8");
}
}
启示
在使用cas过程中有些配置不起作用需要跟踪到源码里看看,有可能是版本不同的问题。
单点登录(十六)-----遇到问题-----cas4.2.x登录成功后报错No principal was found---cas中文乱码问题完美解决的更多相关文章
- 遇到问题-----cas4.2.x登录成功后报错No principal was found---cas中文乱码问题完美解决
情况 我们之前已经完成了cas4.2.x登录使用MongoDB验证方式并且自定义了加密. 单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密 但是悲剧的是 ...
- 十六:SQL注入之查询方式及报错盲注
在很多注入时,有很多注入会出现无回显的情况,其中不回显的原因可能是SQL查询语句有问题,这时候我们需要用到相关的报错或者盲注进行后续操作,同时作为手工注入的时候,需要提前了解SQL语句能更好的选择对应 ...
- 单点登录(十七)----cas4.2.x登录mongodb验证方式成功后返回更多信息更多属性到客户端
我们在之前已经完成了cas4.2.x登录使用mongodb验证方式登录成功了.也解决了登录名中使用中文乱码的问题. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方 ...
- 单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密
我们在前一篇文章中实现了cas4.2.x登录使用mongodb验证方式. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 也学习参考了cas5.0.x版 ...
- 单点登录(十八)----cas4.2.x客户端增加权限控制shiro
我们在上面章节已经完成了cas4.2.x登录启用mongodb的验证方式. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 也完成了获取管理员身份属性 ...
- 单点登录(十四)-----实战-----cas5.0.x登录mongodb验证方式常规的四种加密的思考和分析
我们在上一篇文章中已经讲解了cas4.2.X登录启用mongodb验证方式 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 但是密码是明文存储的,也就是 ...
- 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程
我们在之前的文章中中已经讲到了正确部署运行cas server 和 在cas client中配置. 在此基础上 我们去掉了https的验证,启用了http访问的模式. 单点登录(七)-----实战-- ...
- .NET Core实战项目之CMS 第十六章 用户登录及验证码功能实现
前面为了方便我们只是简单实现了基本业务功能的增删改查,但是登录功能还没有实现,而登录又是系统所必须的,得益于 ASP.NET Core的可扩展性因此我们很容易实现我们的登录功能.今天我将带着大家一起来 ...
- 【SSO单点系列】(4):CAS4.0 SERVER登录后用户信息的返回
接着上一篇,在上一篇中我们描述了怎么在CAS SERVER登录页上添加验证码,并进行登录.一旦CAS SERVER验证成功后,我们就会跳转到客户端中去.跳转到客户端去后,大家想一想,客户端总要获取用户 ...
随机推荐
- ELK环境搭建
ELK环境搭建 1. Virtualbox/Vagrant安装 41.1. Virtualbox安装 41.2. Vagrant安装 41.2.1. 简述 41.2.2. Vagrant box 41 ...
- Spark Streaming流式处理
Spark Streaming介绍 Spark Streaming概述 Spark Streaming makes it easy to build scalable fault-tolerant s ...
- Yii2 创建新项目目录
默认的高级应用模板包括三个应用 backend – 应用的后台 frontend – 应用的前台 console – 应用的控制台应用 那么如果我们要在增加应用呢?比如在加一个手机端的应用,或者后台和 ...
- linux cat显示若干行
[一]从第3000行开始,显示1000行.即显示3000~3999行 cat filename | tail -n +3000 | head -n 1000 [二]显示1000行到3000行 cat ...
- JVM新生代老年代详解
1.为什么会有年轻代 我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我 ...
- java分布式事务,及解决方案
1.什么是分布式事务 分布式事务就是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上.以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成 ...
- b6
吴晓晖(组长) 过去两天完成了哪些任务 对手写输入进行了重构,然后重新捋了一下bayes的思路 展示GitHub当日代码/文档签入记录 接下来的计划 推荐算法 还剩下哪些任务 组员:刘帅珍 过去两天完 ...
- AttributeError: module ‘tensorflow.python.ops.nn’ has no attribute ‘leaky_relu’
#AttributeError: module 'tensorflow.python.ops.nn' has no attribute 'leaky_relu' 的原因主要是版本的问题 解决方法是更新 ...
- 22_IO_第22天(File、递归)_讲义
今日内容介绍 1.File 2.递归 xmind:下载地址: 链接:https://pan.baidu.com/s/1Eaj9yP5i0x4PiJsZA4StQg 密码:845a 01IO技术概述 * ...
- QVariant相当于一个包含大多数Qt数据类型的联合体(源码解读)
将数据存储为一个Private结构体类型的成员变量d: <qvariant.cpp> 1 QVariant::QVariant(Type type) 2 { create(type, 0) ...