CAS 单点登录4.24版本 登录调用其它系统并且返回客户端用其它的用户信息改造
1.登录调用其它系统。修改deployerConfigContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- 引入配置文件 --> <!-- <bean id="propertyConfigurer" --> <!-- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> --> <!-- <property name="location" value="classpath:redis.properties" /> --> <!-- </bean> --> <util:map id="authenticationHandlersResolvers"> <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" /> <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> </util:map> <util:list id="authenticationMetadataPopulators"> <ref bean="successfulHandlerMetaDataPopulator" /> <ref bean="rememberMeAuthenticationMetaDataPopulator" /> </util:list> <!-- <alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" /> --> <!--begin 从数据库中的用户表中读取 --> <bean id="callIntefaceAuthenticationHandler" name="primaryAuthenticationHandler" class="com.hivecas.adaptors.CallIntefaceAuthenticationHandler"> </bean> <alias name="userInfoPrincipalResolver" alias="primaryPrincipalResolver" /> <!-- <alias name="personDirectoryPrincipalResolver" alias="primaryPrincipalResolver" /> --> <bean id="attributeRepository" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao" p:backingMap-ref="attrRepoBackingMap" /> <util:map id="attrRepoBackingMap"> <entry key="uid" value="uid" /> <entry key="eduPersonAffiliation" value="eduPersonAffiliation" /> <entry key="groupMembership" value="groupMembership" /> <entry> <key><value>memberOf</value></key> <list> <value>faculty</value> <value>staff</value> <value>org</value> </list> </entry> </util:map> <!--end 从数据库中的用户表中读取 --> <alias name="serviceThemeResolver" alias="themeResolver" /> <alias name="jsonServiceRegistryDao" alias="serviceRegistryDao" /> <!-- <alias name="defaultTicketRegistry" alias="ticketRegistry" /> --> <!-- 票据保存方式及有效期设置 --> <alias name="redisTicketRegistry" alias="ticketRegistry" /> <!-- <bean id="redisTicketRegistry" class="com.hivescm.cas.ticket.registry.RedisTicketRegistry" --> <!-- p:client-ref="ticketRedisTemplate" --> <!-- p:tgtTimeout="28800" --> <!-- p:stTimeout="10"/> --> <bean id="redisTicketRegistry" class="com.hivescm.cas.ticket.registry.RedisClusterTicketRegistry" p:client-ref="redisTemplate" p:tgtTimeout="28800" p:stTimeout="1000"/> <!-- redis连接池 --> <!-- jedis 配置 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" > <!-- 最大空闲数 --> <property name="maxIdle" value="${redis.maxIdle}" /> <!-- 最大建立连接等待时间 --> <property name="maxWaitMillis" value="${redis.maxWait}" /> <!-- 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 --> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean > <!-- 配置文件加载 --> <bean id="resourcePropertySource" class="org.springframework.core.io.support.ResourcePropertySource"> <constructor-arg name="name" value="redis.cluster.properties"/> <constructor-arg name="resource" value="classpath:redis.cluster.properties"/> </bean> <!-- redisCluster配置 --> <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration"> <constructor-arg name="propertySource" ref="resourcePropertySource"/> </bean> <!-- redis服务器中心 --> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/> <constructor-arg name="poolConfig" ref="poolConfig"/> <property name="password" value="${redis.password}" /> <property name="timeout" value="${redis.timeout}" ></property> </bean > <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" > <property name="connectionFactory" ref="connectionFactory" /> <!-- 如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! --> <property name="keySerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer" > <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> </bean > <alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" /> <!-- <alias name="multiTimeUseOrTimeoutExpirationPolicy" alias="serviceTicketExpirationPolicy" /> --> <alias name="neverExpiresExpirationPolicy" alias="serviceTicketExpirationPolicy" /> <alias name="anyAuthenticationPolicy" alias="authenticationPolicy" /> <alias name="acceptAnyAuthenticationPolicyFactory" alias="authenticationPolicyFactory" /> <bean id="auditTrailManager" class="org.jasig.inspektr.audit.support.Slf4jLoggingAuditTrailManager" p:entrySeparator="${cas.audit.singleline.separator:|}" p:useSingleLine="${cas.audit.singleline:false}"/> <alias name="neverThrottle" alias="authenticationThrottle" /> <util:list id="monitorsList"> <ref bean="memoryMonitor" /> <ref bean="sessionMonitor" /> </util:list> <alias name="defaultPrincipalFactory" alias="principalFactory" /> <alias name="defaultAuthenticationTransactionManager" alias="authenticationTransactionManager" /> <alias name="defaultPrincipalElectionStrategy" alias="principalElectionStrategy" /> <alias name="tgcCipherExecutor" alias="defaultCookieCipherExecutor" /> </beans>
2.CallIntefaceAuthenticationHandler
package com.hivecas.adaptors; import org.apache.commons.lang3.StringUtils; import org.jasig.cas.authentication.HandlerResult; import org.jasig.cas.authentication.PreventedException; import org.jasig.cas.authentication.UsernamePasswordCredential; import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSONObject; import com.hivecas.model.UsernamePasswordStrongCredential; import com.hivecas.util.HttpUtil; import com.hivescm.common.domain.DataResult; import javax.security.auth.login.AccountNotFoundException; import javax.security.auth.login.FailedLoginException; import javax.validation.constraints.NotNull; import java.security.GeneralSecurityException; import java.util.HashMap; import java.util.Map; /** * 自定义请求接口验证用户登录权限 * @author lvxt * */ @Component("callIntefaceAuthenticationHandler") public class CallIntefaceAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler { @NotNull private String requestUrl; @Override protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential) throws GeneralSecurityException, PreventedException { if (StringUtils.isBlank(requestUrl)) { throw new GeneralSecurityException("Authentication handler is not configured correctly"); } final String username = credential.getUsername(); final String password = credential.getPassword(); Map dataMap; UsernamePasswordStrongCredential strongCredential; try { Map map =new HashMap(); map.put("loginName", username); map.put("password", password); String resultJsonStr=HttpUtil.postRequest(requestUrl, map); DataResult data= (DataResult) JSONObject.parseObject(resultJsonStr, DataResult.class); if (!data.isSuccess()) { throw new FailedLoginException("Password does not match value on record."); } strongCredential=(UsernamePasswordStrongCredential) credential; dataMap=(Map) data.getResult(); strongCredential.setAttributes(dataMap); } catch (final IncorrectResultSizeDataAccessException e) { if (e.getActualSize() == 0) { throw new AccountNotFoundException(username + " not found with SQL query"); } else { throw new FailedLoginException("Multiple records found for " + username); } } catch (final DataAccessException e) { throw new PreventedException("SQL exception while executing query for " + username, e); } return createHandlerResult(strongCredential, this.principalFactory.createPrincipal(username,dataMap), null); } public String getRequestUrl() { return requestUrl; } @Autowired public void setRequestUrl(@Value("${cas.login.auth.request.url:}") String requestUrl) { this.requestUrl = requestUrl; } }
3.UsernamePasswordStrongCredential的改造,用于存入调用登录接口返回的用户信息存入map中
package com.hivecas.model; import java.util.HashMap; import java.util.Map; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.jasig.cas.authentication.UsernamePasswordCredential; public class UsernamePasswordStrongCredential extends UsernamePasswordCredential { /** * 带验证码的登录界面 */ private static final long serialVersionUID = 1L; /** 验证码*/ @Size(min = 1, message = "required.authcode") private String authcode; /**登录类型 * 1.pc登录 2 移动端登录 */ @NotNull @Size(min = 1, message = "required.loginType") private String loginType; /** * 用户返回数据 */ private Map<String,Object> attributes = new HashMap<>(); public Map<String, Object> getAttributes() { return attributes; } public void setAttributes(Map<String, Object> attributes) { this.attributes = attributes; } public String getLoginType() { return loginType; } public void setLoginType(String loginType) { this.loginType = loginType; } /** * * @return */ public final String getAuthcode() { return authcode; } /** * * @param authcode */ public final void setAuthcode(String authcode) { this.authcode = authcode; } public UsernamePasswordStrongCredential() { super(); } public UsernamePasswordStrongCredential(String userName, String password) { super(userName, password); } public UsernamePasswordStrongCredential(String userName, String password,String loginType) { super(userName, password); setLoginType(loginType); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final UsernamePasswordStrongCredential that = (UsernamePasswordStrongCredential) o; if (getPassword() != null ? !getPassword().equals(that.getPassword()) : that.getPassword() != null) { return false; } if (getPassword() != null ? !getPassword().equals(that.getPassword()) : that.getPassword() != null) { return false; } if (authcode != null ? !authcode.equals(that.getAuthcode()) : that.getAuthcode() != null) return false; return true; } @Override public int hashCode() { return new HashCodeBuilder().append(getUsername()) .append(getPassword()).append(authcode).toHashCode(); } }
4.UserInfoPrincipalResolver.java
package org.jasig.cas.authentication.principal; import javax.validation.constraints.NotNull; import org.jasig.cas.authentication.Credential; import org.jasig.cas.authentication.principal.DefaultPrincipalFactory; import org.jasig.cas.authentication.principal.Principal; import org.jasig.cas.authentication.principal.PrincipalFactory; import org.jasig.cas.authentication.principal.PrincipalResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.hivecas.model.UsernamePasswordStrongCredential; @Component("userInfoPrincipalResolver") public class UserInfoPrincipalResolver implements PrincipalResolver{ protected final transient Logger logger = LoggerFactory.getLogger(this.getClass()); /** * Factory to create the principal type. **/ @NotNull protected PrincipalFactory principalFactory = new DefaultPrincipalFactory(); @Override public Principal resolve(Credential credential) { logger.debug("Attempting to resolve a principal..."); final String principalId = extractPrincipalId(credential); UsernamePasswordStrongCredential strongCredential=(UsernamePasswordStrongCredential) credential; return this.principalFactory.createPrincipal(principalId, strongCredential.getAttributes()); } @Override public boolean supports(Credential credential) { return credential != null && credential.getId() != null; } /** * Extracts the id of the user from the provided credential. This method should be overridden by subclasses to * achieve more sophisticated strategies for producing a principal ID from a credential. * * @param credential the credential provided by the user. * @return the username, or null if it could not be resolved. */ protected String extractPrincipalId(final Credential credential) { return credential.getId(); } }
5.页面2.0文件下的casServiceValidationSuccess.jsp改造
<%@ page session="false" contentType="application/xml; charset=UTF-8" %> <%@ page import="java.util.*, java.util.Map.Entry" %> <%@ 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> <cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}</cas:user> <c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes)> 0}"> <cas:attributes> <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"> <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}> </c:forEach> </cas:attributes> </c:if> <c:if test="${not empty pgtIou}"> <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket> </c:if> <c:if test="${fn:length(assertion.chainedAuthentications) > 1}"> <cas:proxies> <c:forEach var="proxy" items="${assertion.chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(assertion.chainedAuthentications)-2}" step="1"> <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy> </c:forEach> </cas:proxies> </c:if> </cas:authenticationSuccess> </cas:serviceResponse>
6.客户端测试页面index.jsp的取值效果
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="org.jasig.cas.client.authentication.AttributePrincipal" %> <%@ page import="java.util.Map" %> <%@ page import="java.lang.String" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>首页</title> </head> <body> <div style="padding-left: 30%"> 用户名: <%=request.getAttribute("name")%> <h1>订单系统<%=request.getLocalPort() %></h1> <% Cookie[] array =request.getCookies(); String JSESSIONID=""; if(array!=null) for(Cookie ck:array) { String ckName=ck.getName(); if(ckName.equals("JSESSIONID")) { JSESSIONID=ck.getValue(); } } %> <% request.setCharacterEncoding("UTF-8"); AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal(); Map attributes = principal.getAttributes(); String id=(String)attributes.get("id"); String phone=(String)attributes.get("phone"); String email=(String)attributes.get("email"); %> Cookie中的JSESSIONID=<%=JSESSIONID%><br> 用户id=<%=id%><br> 用户手机号=<%=phone%><br> 用户email=<%=email%><br> <br><br> <a href="http://passport.hivescm.com/cas/logout" >退出</a> </div> </body> </html>