1. 程式人生 > >CAS SSO 4.0 單點登入返回更多使用者資訊

CAS SSO 4.0 單點登入返回更多使用者資訊

從cas server登入成功後,預設只能從cas server得到使用者名稱。但程式中也可能遇到需要得到更多如姓名,手機號,email等更多使用者資訊的情況。
cas client拿到使用者名稱後再到資料庫中查詢,的確可以得到關於該使用者的更多資訊。

但是如果使用者登入成功後,直接從cas server返回給cas client使用者的詳細資訊,這也是一個不錯的做法。這個好處,尤其是在分散式中得以彰顯,cas server可以把使用者資訊傳遞給各個應用系統,如果是上面那種做法,那麼各個系統得到使用者名稱後,都得去資料庫中查詢一遍,無疑是一件重複性工作。

一、首先需要配置屬性attributeRepository:

WEB-INF目錄找到 deployerConfigContext.xml檔案,同時配置 attributeRepository 如下:

<bean  class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao" id="attributeRepository">
	<constructor-arg index="0" ref="dataSource"/>
	<constructor-arg index="1" value="select * from t_user where {0}"/>
	<property name="queryAttributeMapping">
		<map>
			<!--這裡的key需寫username和登入頁面一致,value對應資料庫使用者名稱欄位-->
			<entry key="username" value="loginname"/>
		</map>
	</property>
	<property name="resultAttributeMapping">
		<map>
			<!--key為對應的資料庫欄位名稱,value為提供給客戶端獲取的屬性名字,系統會自動填充值-->
			<entry key="suid" value="suid"/>
		</map>
	</property>
	<!--  
	<property name="queryType">
		<value>OR</value>
	</property> 
	-->
</bean>

切記:查詢出來的欄位名中間不能使用 _ (下劃線),否則獲取不到資料,如 cell_phone 需要 設定別名為 cellPhone.queryAttributeMapping是組裝sql用的查詢條件屬性,上述配置後,結合封裝成查詢sql就是 

select* from userinfo where loginname=#username#resultAttributeMapping是sql執行完畢後返回的結構屬性, key對應資料庫欄位,value對應客戶端獲取引數。如果要組裝多個查詢條件,需要加上下面這個,預設為AND.

<property name="queryType">
	<value>OR</value>
</property> 
二、修改serviceRegistryDao

deployerConfigContext.xml中的 beans中id為serviceRegistryDao的屬性 registeredServicesList。在 registeredServicesList中新增allowedAttributes屬性的值。列出的每個值,在客戶端就可以訪問了。

<util:list id="registeredServicesList">
	<bean class="org.jasig.cas.services.RegexRegisteredService"
		  p:id="0" p:name="HTTP and IMAP" p:description="Allows HTTP(S) and IMAP(S) protocols"
		  p:serviceId="^(http?|https?|imaps?)://.*" p:evaluationOrder="10000001">
		  <!-- 新增該屬性allowedAttributes -->
		  <property name="allowedAttributes">
			  <list>
				  <value>suid</value>
			  </list>
		  </property>
	</bean>
</util:list>
此步驟非常重要,可以看看org.jasig.cas.services.RegexRegisteredService的原始碼,其中的allowedAttributes是關鍵。

三、修改casServiceValidationSuccess.jsp

WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp.在server驗證成功後,這個頁面負責生成與客戶端互動的xml資訊,在預設的casServiceValidationSuccess.jsp中,只包括使用者名稱,並不提供其他的屬性資訊,因此需要對頁面進行擴充套件。如下:

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

四、CAS Client獲取使用者資訊
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
Map attributes = principal.getAttributes();
String email = attributes.get("suid"); 

補充:自定義SQL查詢使用者資訊

在步驟一,使用SQL比較單一,這樣我就發現了類org.jasig.services.persondir.support.jdbc.NamedParameterJdbcPersonAttributeDao這個類中有DataSource和sql.但是我發現使用這個類我無法使用自定義查詢SQL因為不能隨便自己來傳入引數,所以可以自己寫類實現類org.jasig.services.persondir.support.AbstractDefaultAttributePersonAttributeDao。具體程式碼如下:

package org.jasig.cas.myself.persondir.support;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.jasig.services.persondir.IPersonAttributes;
import org.jasig.services.persondir.support.AbstractDefaultAttributePersonAttributeDao;
import org.jasig.services.persondir.support.CaseInsensitiveNamedPersonImpl;
import org.jasig.services.persondir.support.IUsernameAttributeProvider;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.google.common.collect.Lists;

public class MyNamedParameterJdbcPersonAttributeDao extends AbstractDefaultAttributePersonAttributeDao implements InitializingBean {

    private JdbcTemplate jdbcTemplate;

    private DataSource dataSource;
    private String sql;
    private IUsernameAttributeProvider usernameAttributeProvider;
    private Set<String> availableQueryAttributes = null;  // default
    private Set<String> userAttributeNames = null;  // default

    @Required
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Required
    public void setSql(String sql) {
        this.sql = sql;
    }
    
    @Required
    public void setUsernameAttributeProvider(IUsernameAttributeProvider usernameAttributeProvider) {
        this.usernameAttributeProvider = usernameAttributeProvider;
    }
    
    public void setAvailableQueryAttributes(Set<String> availableQueryAttributes) {
        this.availableQueryAttributes = Collections.unmodifiableSet(availableQueryAttributes);
    }
    
    @Required
    public void setUserAttributeNames(Set<String> userAttributeNames) {
        this.userAttributeNames = Collections.unmodifiableSet(userAttributeNames);
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public Set<String> getAvailableQueryAttributes() {
        return availableQueryAttributes;
    }

    @Override
    public Set<IPersonAttributes> getPeopleWithMultivaluedAttributes(Map<String, List<Object>> queryParameters) {
        String username = usernameAttributeProvider.getUsernameFromQuery(queryParameters);
        Map<String,List<Object>> mapOfLists = new HashMap<String,List<Object>>();
        Object suid = jdbcTemplate.queryForObject(sql, new RowMapper<Object>() {

            @Override
            public Object mapRow(ResultSet paramResultSet, int paramInt) throws SQLException {
                // TODO Auto-generated method stub
                return paramResultSet.getObject("SUID");
            }}, new Object[]{username,username,username});
        if(null != suid){
            mapOfLists.put("suid", Lists.newArrayList(suid));
        }
        return getResults(username, mapOfLists);
    }

    @Override
    public Set<String> getPossibleUserAttributeNames() {
        return userAttributeNames;
    }

    public Set<IPersonAttributes> getResults(String username, Map<String, List<Object>> attributes) {
        IPersonAttributes person = new CaseInsensitiveNamedPersonImpl(username, attributes);
        return Collections.singleton(person);
    }

}

然後在deployerConfigContext.xml檔案做如下修改:
    <bean id="attributeRepository" class="org.jasig.cas.myself.persondir.support.MyNamedParameterJdbcPersonAttributeDao"> 
        <property name="dataSource" ref="dataSource"/>
        <property name="sql" value="select suid from login where NAME = ? or CELLPHONE = ? or EMAIL = ?" />
        <property name="usernameAttributeProvider" ref="usernameAttributeProvider"/>
        <property name="userAttributeNames">
            <value>suid</value>
        </property>
    </bean>

    <bean id="usernameAttributeProvider" class="org.jasig.services.persondir.support.SimpleUsernameAttributeProvider" />

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${cust.center.jdbc.driverClassName}" />
        <property name="jdbcUrl" value="${cust.center.jdbc.url}" />
        <property name="user" value="${cust.center.jdbc.username}" />
        <property name="password" value="${cust.center.jdbc.password}" />
        <property name="preferredTestQuery" value="SELECT 1"/>
        <property name="idleConnectionTestPeriod" value="18000"/>
        <property name="testConnectionOnCheckout" value="true"/>
    </bean>

這樣自定義查詢方式就完成了,也可以返回更多使用者資訊。當然我這是隻返回一個多餘資訊。如果你需要返回多個,可以做相應的修改哈。