Java web集成Cas单点登录

一、概述

现在越来越多的系统提供了统一身份的认证-单点登录机制,新的应用只需要集成单点登录即可轻松集成到已有的系统中。这里我们采用Cas3.2.1来进行演示,注:不同版本的配置会有所区别。

二、Java集成Cas单点登录

2.1 拷贝提供的jar

将下载好的cas-client-core-3.2.1.jar文件拷贝到客户端应用所在服务器中,并将存放这些jar包的路径设置到应用的classpath中。比如WebContent\WEB-INF\lib中。

2.2 修改web.xml文件

在新应用的web.xml中添加以下内容:

<!-- ==================== 单点登录开始 ================ -->
    <!-- 用于单点退出,该监听器和过滤器用于实现单点登出功能,可选配置 -->	
    <listener>
		<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
	</listener>
	<filter>
		<filter-name>CAS Single Sign Out Filter</filter-name>
		<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>CAS Single Sign Out Filter</filter-name>
		<url-pattern>/*</url-pattern>
</filter-mapping>
	<!-- 该过滤器负责用户的认证工作,必须启用它 -->
	<filter>
		<filter-name>CASFilter</filter-name>
		<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
		<init-param>
			<param-name>casServerLoginUrl</param-name>
			<!--这里的server是CAS服务端的登录地址,login为固定值-->
			<param-value>http://id6.wsedu.com/authserver/login</param-value>
		</init-param>
		<init-param>
			<param-name>serverName</param-name>
			<!--这里是应用地址,注意是域名:端口或者ip:端口-->
			<param-value>http://ssodemo.wsedu.com:8080</param-value>
        </init-param>
	</filter>
	<filter-mapping>
		<filter-name>CASFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<!-- 该过滤器负责对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>
			<!--这里的server是CAS服务端的地址,这里不要加login-->
			<param-value>http://id6.wsedu.com/authserver</param-value>
		</init-param>
		<init-param>
			<param-name>serverName</param-name>
			<!--这里是应用地址,注意是域名:端口或者ip:端口-->
			<param-value>http://ssodemo.wsedu.com: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>
<!-- 该过滤器负责实现HttpServletRequest请求的包裹,比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名。 -->
	<filter>
		<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
		<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!-- =============== 单点登录结束 ================= -->

说明:
web项目的编码都encodeFilter的filter-mapping都是放到第一个的,这个执行顺序要注意的。Casclient客户端是不涉及到编码格式的,默认都是来自于项目的。
执行第一个getParameter()的时候,java将会按照编码分析所有的提交内容,而后续的getParameter()不再进行分析,所以setCharacterEncoding()无效。而对于GET方法提交表单是,提交的内容在URL中,一开始就已经按照编码分析提交内容,setCharacterEncoding()自然就无效。
所以请将如上的代码filter-mapping放到项目编码filter-mapping下面,保证最先执行项目编码过滤器。

2.3 忽略拦截指定url

默认情况下,cas会拦截所有的url,然而,我们有时需要放过部分请求,这时,就需要进行自定义认证过滤器AuthenticationFilter。举例:接入微信支付,微信的异步回调不需要认证。
自定义AuthenticationFilterExcludeUrl.java:

package com.tc.interceptors;

import org.apache.log4j.Logger;
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;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;


/**
 * 重写cas-AuthenticationFilter 不拦截微信异步回调url
 */
public class AuthenticationFilterExcludeUrl extends AbstractCasFilter {
    private static Logger logger = Logger.getLogger(AuthenticationFilterExcludeUrl.class);

    private String casServerLoginUrl;
    private boolean renew = false;
    private boolean gateway = false;
    private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
    private String[] excludePaths;


    public AuthenticationFilterExcludeUrl() {
    }


    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);
                }
            }
        }
        String _excludePaths = getPropertyFromInitParams(filterConfig, "exceptPaths", null);
//        logger.info("=================_excludePaths:" + _excludePaths);
        if (CommonUtils.isNotBlank(_excludePaths)) { // 1.获取不需拦截的url
            excludePaths = _excludePaths.trim().split(",");
//            logger.info("=================excludePaths[0]:" + excludePaths[0]);
        }
    }

    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;

        String uri = request.getRequestURI();
//        logger.info("==========AuthentificationFilterExcludeUrl-doFilter-uri: " + uri);
        if (excludePaths != null && excludePaths.length > 0 && uri != null) {
            for (String path : excludePaths) {
                if (CommonUtils.isNotBlank(path)) {
                    if (uri.contains(path)) { // 2.放过不需拦截的url
                        filterChain.doFilter(request, response);
                        return;
                    }
                }
            }
        }

        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 + "\"");
                }

                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;
    }

    public String[] getExcludePaths() {
        return excludePaths;
    }

    public void setExcludePaths(String[] excludePaths) {
        this.excludePaths = excludePaths;
    }
}

将web.xml中的

<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>

改为:

 <filter-class>com.wasion.interceptors.AuthenticationFilterExcludeUrl</filter-class>

并且在该过滤器配置中增加:

<init-param>
           <description>cas not filter url</description>
           <param-name>exceptPaths</param-name>
           <param-value>/api/wxpay/notifyUrl</param-value>
       </init-param>

完整配置如下:

<filter>
      <filter-name>CASFilter</filter-name>
      <filter-class>com.wasion.interceptors.AuthenticationFilterExcludeUrl</filter-class>
      <init-param>
          <param-name>casServerLoginUrl</param-name>
          <!--这里的server是CAS服务端的登录地址,login为固定值-->
          <param-value>https://authserver.eut.edu.cn/authserver/login</param-value>
      </init-param>
      <init-param>
          <param-name>serverName</param-name>
          <!--这里是应用地址,注意是域名:端口或者ip:端口-->
          <param-value>https://xxxx.eut.edu.cn:443</param-value>
      </init-param>
      <init-param>
          <description>cas not filter url</description>
          <param-name>exceptPaths</param-name>
          <param-value>/api/wxpay/notifyUrl</param-value>
      </init-param>
  </filter>
  

三、获取用户信息

第三方应用在配置好web.xml之后,需要调整代码,用集成之后的方式获取当前登录用户。获取用户信息的示例代码如下:

//cas-client-3.2.1版本集成
	String uid = request.getRemoteUser();
    String cn = "";
    Principal principal = request.getUserPrincipal();
    if(principal!=null && principal instanceof AttributePrincipal){
    	AttributePrincipal aPrincipal = (AttributePrincipal)principal;
        //获取用户信息中公开的Attributes部分
    	Map<String, Object> map = aPrincipal.getAttributes();
        // 获取姓名,可以根据属性名称获取其他属性
        cn = (String)map.get("cn");
    }

四、集成应用退出

第三方应用在退出的时候需要重定向跳转到IDS的退出地址,具体示例代码如下(红色部分需要根据实际情况调整):

session.invalidate();
    // ids的退出地址,id6.wsedu.com为ids的域名  authserver为ids的上下文,logout为固定值
	String casLogoutURL = "http://id6.wsedu.com/authserver/logout";
    // service后面带的参数为应用的访问地址,需要使用URLEncoder进行编码
    String redirectURL=casLogoutURL+"?service="+URLEncoder.encode("http://ssodemo.wsedu.com:8080/caslogin.jsp");
	response.sendRedirect(redirectURL);

五、认证接口工作过程

在这里插入图片描述

六、集成应用添加SSL证书信任

统一身份的集成,待集成的应用系统需要去统一身份平台验证令牌ticket,这个验证如果统一身份平台配置了https,则需要在第三方应用服务器导入SSL证书添加信任,一般情况下,java程序在该第三方应用所使用的jdk中把证书导入security\cacerts即可。
安装证书举例,360浏览器为例
第一步:首先,需要用浏览器下载https证书,访问https://uis.fudan.edu.cn
第二步:切换到证书的详细信息,并选择复制到文件
第三步:按证书导出向导,下一步选择证书格式,一般选择缺省的证书格式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第六步:复制证书到jdk环境下,例如:/opt/soft/fudan.cer
第七步:使用命令行 cd $JAVA_HOME/security/cacerts

$JAVA_HOME替换为你自己的JDK目录
/usr/java/jdk1.7.0_80/jre/lib/security
第八步:输入命令
keytool -import -alias cacerts -keystore cacerts -file /opt/soft/fudan.cer
第九步:按提示输入cacerts证书库的密码,缺省为changeit
第十步:信任证书,导入完成。
第十一步:查看导入的证书,输入keytool -list -keystore cacerts
注: 更新证书前需输入keytool -delete -alias cacerts -keystore cacerts 先删除证书

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值