情况

我们之前已经完成了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。

  1. <dependency>
  2. <groupId>org.jasig.cas.client</groupId>
  3. <artifactId>cas-client-core</artifactId>
  4. <version>3.4.1</version>
  5. </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>

修改后的配置为:

  1. <!-- 该过滤器负责对Ticket的校验工作,必须-->
  2. <filter>
  3. <filter-name>CAS Validation Filter</filter-name>
  4. <filter-class>
  5. org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
  6. </filter-class>
  7. <init-param>
  8. <param-name>casServerUrlPrefix</param-name>
  9. <param-value>http://127.0.0.1:8080/cas/</param-value>
  10. </init-param>
  11. <init-param>
  12. <param-name>serverName</param-name>
  13. <param-value>http://127.0.0.1:8080</param-value>
  14. </init-param>
  15. <init-param>
  16. <param-name>encoding</param-name>
  17. <param-value>utf-8</param-value>
  18. </init-param>
  19. </filter>
  20. <filter-mapping>
  21. <filter-name>CAS Validation Filter</filter-name>
  22. <!-- 对项目中的哪些路径做登录拦截-->
  23. <url-pattern>/*</url-pattern>
  24. </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最终配置为

  1. <%@ page session="false" contentType="application/xml; charset=utf-8" import="java.net.URLEncoder" %>
  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  3. <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
  4. <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
  5. <cas:authenticationSuccess>
  6. <c:set var="val" value="${fn:escapeXml(principal.id)}"/>
  7. <cas:user><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:user>
  8. <c:if test="${not empty pgtIou}">
  9. <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
  10. </c:if>
  11. <c:if test="${fn:length(chainedAuthentications) > 0}">
  12. <cas:proxies>
  13. <c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(chainedAuthentications)}" step="1">
  14. <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
  15. </c:forEach>
  16. </cas:proxies>
  17. </c:if>
  18. <cas:attributes>
  19. <c:forEach items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"var="attr">
  20. <c:set var="val" value="${attr.value}"/>
  21. <cas:${attr.key}><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:${attr.key}>
  22. </c:forEach>
  23. </cas:attributes>
  24. </cas:authenticationSuccess>
  25. </cas:serviceResponse>

只获取id的casServiceValidationSuccess.jsp最终配置为

  1. <%@ page session="false" contentType="application/xml; charset=utf-8" import="java.net.URLEncoder" %>
  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  3. <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
  4. <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
  5. <cas:authenticationSuccess>
  6. <c:set var="val" value="${fn:escapeXml(principal.id)}"/>
  7. <cas:user><%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%></cas:user>
  8. <c:if test="${not empty pgtIou}">
  9. <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
  10. </c:if>
  11. <c:if test="${fn:length(chainedAuthentications) > 0}">
  12. <cas:proxies>
  13. <c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(chainedAuthentications)}" step="1">
  14. <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
  15. </c:forEach>
  16. </cas:proxies>
  17. </c:if>
  18. </cas:authenticationSuccess>
  19. </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中文乱码问题完美解决的更多相关文章

  1. 单点登录(十六)-----遇到问题-----cas4.2.x登录成功后报错No principal was found---cas中文乱码问题完美解决

    情况 我们之前已经完成了cas4.2.x登录使用mongodb验证方式并且自定义了加密. 单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密 但是悲剧的是 ...

  2. 单点登录(十七)----cas4.2.x登录mongodb验证方式成功后返回更多信息更多属性到客户端

    我们在之前已经完成了cas4.2.x登录使用mongodb验证方式登录成功了.也解决了登录名中使用中文乱码的问题. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方 ...

  3. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程

    我们在之前的文章中中已经讲到了正确部署运行cas server 和 在cas client中配置. 在此基础上 我们去掉了https的验证,启用了http访问的模式. 单点登录(七)-----实战-- ...

  4. 2、cas4.0 单点登录 之 cas-client

    cas4.0 单点登录 之 cas-client cas4.0 单点登录 之 https证书已经做好了证书的准备工作.如今结合cas-server来配置单点登录: 一.安装cas服务端(cas-ser ...

  5. 苹果手机Safari无痕浏览模式下系统登录成功但是页面不跳转

    昨天下午,测试提了一个bug,问题是:在苹果手机Safari无痕浏览模式下系统登录成功但是页面不跳转. 思前想后找了半天没思路,后来经过同事的点拨,说可能是禁用了cookie之类的,反正我也没思路就顺 ...

  6. 结合数据库登录注册模块,登录成功之后跳到WebView

    最近刚刚做了一个模块,在本地建立一个数据库,存储注册的账号,登录的时候取出,正确则登录,登录之后跳到一个webView网页. 直接上代码吧. LoginActivity.java package co ...

  7. spring security为不同用户显示各自的登录成功页面

    一个常见的需求是,普通用户登录之后显示普通用户的工作台,管理员登陆之后显示后台管理页面.这个功能可以使用taglib解决. 其实只要在登录成功后的jsp页面中使用taglib判断当前用户拥有的权限进行 ...

  8. 【转】【可用】Android 登录判断器,登录成功后帮你准确跳转到目标activity

    我们在使用应用时肯定遇到过这样的情景,打开应用,并不是需要我们登录,你可以浏览应用中的大部分页面,但是当你想看某个详情页的时候,点击后突然跳转到了登录页面,好,我们输入账号密码,点击登录,登录成功,跳 ...

  9. [原]基于CAS实现单点登录(SSO):登录成功后,cas client如何返回更多用户信息

    从cas server登录成功后,默认只能从casclient得到用户名.但程序中也可能遇到需要得到更多如姓名,手机号,email等更多用户信息的情况. cas client拿到用户名后再到数据库中查 ...

随机推荐

  1. lombok 的使用

    参考:https://blog.csdn.net/motui/article/details/79012846

  2. VS2013默认打开HTML文件没有设计视图

    打开VS菜单->工具->选项->文本编辑器->文件扩展名,右侧输入html,再下拉列表选HTML(Web窗体)编辑器,点添加,确定. 第二条是彻底解决VS2013不能编辑HTM ...

  3. javascript 特定字符分隔字符串函数

    function fn(num,div,token){//num需要分割的数字,div多少位分割 token分割字符 num=num+'',div=div||3,token=token||',' re ...

  4. Spring cloud config-client 爬坑

    配置文件 找不到属性 Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'from' in st ...

  5. UNITY插件信息收集

    2018.8.7 UNITY超级优化神器 : Amplify Impostors

  6. 在 springboot 中如何整合 shiro 应用 ?

     Shiro是Apache下的一个开源项目,我们称之为Apache Shiro. 它是一个很易用与Java项目的的安全框架,提供了认证.授权.加密.会话管理,与spring Security 一样都是 ...

  7. JavaWeb--过滤器Filter (一)

    过滤器是在服务器上运行的,并且位于请求和响应中间起过滤功能的程序.其工作原理如下图所示:   在与过滤器相关联俄Servlet或JSP运行前,过滤器先执行.一个过滤器可以一个或多个Servlet或JS ...

  8. Air File.load加载问题

    不要用File.load同时加载多个文件,有时会引起程序崩溃. 在需要同时加载多个文件时,要一个一个排队加载,等到上一个加载完成再加载下一个. 也可以设定一个static的File,加载前先File. ...

  9. 第七章 二叉搜索树(b1)BST:查找

  10. 在Action中获取表单提交数据

    -----------------siwuxie095 在 Action 中获取表单提交数据 1.之前的 Web 阶段是提交表单到 Servlet,在其中使用 Request 对象 的方法获取数据 2 ...