Java web integration Cas single sign on

I. overview

Now more and more systems provide a unified identity authentication single sign on mechanism. New applications can be easily integrated into existing systems only by integrating single sign on. Here we use Cas3.2.1 for demonstration, note: different versions of the configuration will be different.

2, Java integrated Cas single sign on

2.1 copy the jar provided

Copy the downloaded cas-client-core-3.2.1.jar file to the server where the client application is located, and set the path where these jar packages are stored to the classpath of the application. For example, in WebContent\WEB-INF\lib.

2.2 modifying the web.xml file

Add the following to the new application's web.xml:

<!-- ==================== Single sign on start ================ -->
    <!-- For single point exit, the listener and filter are used to realize single point exit function, optional configuration -->	
    <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>
	<!-- This filter is responsible for user authentication and must be enabled -->
	<filter>
		<filter-name>CASFilter</filter-name>
		<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
		<init-param>
			<param-name>casServerLoginUrl</param-name>
			<!--There server yes CAS Login address of server,login Fixed value-->
			<param-value>http://id6.wsedu.com/authserver/login</param-value>
		</init-param>
		<init-param>
			<param-name>serverName</param-name>
			<!--Here is the application address. Note the domain name:Port or ip:port-->
			<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>

	<!-- This filter is responsible for Ticket Must be enabled -->
	<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>
			<!--There server yes CAS Server address,Don't add here login-->
			<param-value>http://id6.wsedu.com/authserver</param-value>
		</init-param>
		<init-param>
			<param-name>serverName</param-name>
			<!--Here is the application address. Note the domain name:Port or ip:port-->
			<param-value>http://ssodemo.wsedu.com:8080</param-value>
		</init-param>
        <!--You need to add a code to the authentication verification-->
        <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>
<!-- This filter is responsible for implementation HttpServletRequest Requested packages, such as allowing developers to pass HttpServletRequest Of getRemoteUser()Method acquisition SSO The login name of the logged in user. -->
	<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>
	<!-- =============== Single sign on end ================= -->

Explain:
The coding of the web project is the first to encode the filter mapping of the filter. This order of execution should be noted. The Casclient client does not involve the encoding format. By default, it comes from the project.
When the first getParameter() is executed, java will analyze all the submitted content according to the code, while the subsequent getParameter() will not be analyzed, so setCharacterEncoding() is invalid. For the GET method submission form, the submitted content is in the URL, and the submitted content has been analyzed in accordance with the code at the beginning, so setCharacterEncoding() is naturally invalid.
So please put the above code filter mapping under the project code filter mapping to ensure that the project coding filter is executed first.

2.3 ignore blocking the specified url

By default, cas intercepts all URLs. However, we sometimes need to let go of some requests. At this time, we need to customize the authentication filter. For example: access wechat payment, wechat asynchronous callback does not need authentication.
Custom 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;


/**
 * Rewriting CAS authenticationfilter does not block wechat asynchronous callback 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. Get the url that does not need to be blocked
            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. Let go of URLs that don't need to be blocked
                        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;
    }
}

In web.xml

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

Changed to:

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

And add:

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

The complete configuration is as follows:

<filter>
      <filter-name>CASFilter</filter-name>
      <filter-class>com.wasion.interceptors.AuthenticationFilterExcludeUrl</filter-class>
      <init-param>
          <param-name>casServerLoginUrl</param-name>
          <!--There server yes CAS Login address of server,login Fixed value-->
          <param-value>https://authserver.eut.edu.cn/authserver/login</param-value>
      </init-param>
      <init-param>
          <param-name>serverName</param-name>
          <!--Here is the application address. Note the domain name:Port or ip:port-->
          <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>
  

3, Get user information

After the third-party application is configured with web.xml, it needs to adjust the code and obtain the current login user by the way of integration. An example code for obtaining user information is as follows:

//cas-client-3.2.1 version integration
	String uid = request.getRemoteUser();
    String cn = "";
    Principal principal = request.getUserPrincipal();
    if(principal!=null && principal instanceof AttributePrincipal){
    	AttributePrincipal aPrincipal = (AttributePrincipal)principal;
        //Get the Attributes exposed in user information
    	Map<String, Object> map = aPrincipal.getAttributes();
        // Get the name. You can get other properties based on the property name
        cn = (String)map.get("cn");
    }

4, Integrated application exit

When a third-party application exits, it needs to redirect to the exit address of IDS. The specific example code is as follows (the red part needs to be adjusted according to the actual situation):

session.invalidate();
    // The exit address of ids, id6.wsedu.com is the domain name of ids authserver is the context of ids, logout is a fixed value
	String casLogoutURL = "http://id6.wsedu.com/authserver/logout";
    // The parameter after the service is the access address of the application, which needs to be encoded with URLEncoder
    String redirectURL=casLogoutURL+"?service="+URLEncoder.encode("http://ssodemo.wsedu.com:8080/caslogin.jsp");
	response.sendRedirect(redirectURL);

5, Working process of certification interface

6, Integration app add SSL certificate trust

For the integration of unified identity, the application system to be integrated needs to verify token ticket by unified identity platform. If https is configured in the unified identity platform, SSL certificate needs to be imported into the third-party application server to add trust. Generally, java program can import certificate into security\cacerts in the jdk used by the third-party application.
Installation certificate example, 360 browser example
Step 1: first of all, you need to download the HTTPS certificate with a browser, visit https://uis.fudan.edu.cn
Step 2: switch to the certificate details and select Copy to file
Step 3: follow the certificate Export Wizard. Next, select the certificate format. Generally, select the default certificate format



Step 6: copy the certificate to the jdk environment, for example, / opt/soft/fudan.cer
Step 7: use the command line CD $java_home / security / cacerts

Replace $Java home with your own JDK directory
/usr/java/jdk1.7.0_80/jre/lib/security
Step 8: enter the command
keytool -import -alias cacerts -keystore cacerts -file /opt/soft/fudan.cer
Step 9: enter the password of cacerts certificate library as prompted. The default is changeit
Step 10: trust the certificate and import it.
Step 11: view the imported certificate and enter keytool -list -keystore cacerts
Note: before updating the certificate, enter keytool -delete -alias cacerts -keystore cacerts to delete the certificate first

56 original articles published, 25 praised, 330000 visitors+
Private letter follow

Tags: Java Session xml JDK

Posted on Sat, 18 Jan 2020 01:33:34 -0500 by Thoughtless