單點登入CAS6-服務端自定義返回的使用者資訊

bitree1發表於2017-02-15

原理

返回的使用者資訊是在deployerConfigContext.xml中的配置的

既然想自定義返回的使用者資訊,那麼繼承org.jasig.services.persondir.support.StubPersonAttributeDao就好了

1、建立com.jadyer.sso.authentication extends StubPersonAttributeDao並複寫getPerson()方法

  使用@Component(value=”attributeRepository”)定義它的BeanID

2、註釋deployerConfigContext.xml中的<bean
id="attributeRepository">
<util:map
id="attrRepoBackingMap">
配置

3、修改WEB-INFviewjspprotocol2.0casServiceValidationSuccess.jsp(不要改3.0下面的)

具體改動,詳見下方貼出的程式碼

另外:返回給客戶端的相關資訊是由org.jasig.services.persondir.IPersonAttributeDao介面定義的

StubPersonAttributeDao就是IPersonAttributeDao的實現之一

其它實現如SingleRowJdbcPersonAttributeDao、LdapPersonAttributeDao等

所以也可在deployerConfigContext.xml中配置的實現為SingleRowJdbcPersonAttributeDao

也就是<bean id="attributeRepository"
class="org.jasig...persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">

不過,個人覺得這樣不是很靈活,所以就不貼示例程式碼了

程式碼

下面是自定義的控制返回的使用者資訊的UserStubPersonAttributeDao.java

package com.jadyer.sso.authentication;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.jasig.services.persondir.IPersonAttributes;
import org.jasig.services.persondir.support.AttributeNamedPersonImpl;
import org.jasig.services.persondir.support.StubPersonAttributeDao;
import org.springframework.stereotype.Component;
import com.jadyer.sso.model.User;

/**
 * 自定義的返回給客戶端相關資訊
 * Created by 玄玉<https://jadyer.github.io/> on 2015/07/18 17:52.
 */
@Component(value="attributeRepository")
public class UserStubPersonAttributeDao extends StubPersonAttributeDao {
    @Resource
    private UserDaoJdbc userDaoJdbc;
    @Override
    public IPersonAttributes getPerson(String uid) {
        Map<String, List<Object>> attributes = new HashMap<String, List<Object>>();
        try {
            User user = userDaoJdbc.getByUsername(uid);
            attributes.put("userId", Collections.singletonList((Object)user.getUsercode()));
            attributes.put("username", Collections.singletonList((Object)user.getUsername()));
            attributes.put("usernamePlain", Collections.singletonList((Object)URLEncoder.encode(user.getUsernamePlain(), "UTF-8")));
            attributes.put("blogURL", Collections.singletonList((Object)"https://jadyer.github.io/"));
            attributes.put("blogger", Collections.singletonList((Object)URLEncoder.encode("玄玉", "UTF-8")));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new AttributeNamedPersonImpl(attributes);
    }
}

下面是用到的查詢資料庫的UserDaoJdbc.java

package com.jadyer.sso.authentication;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.jadyer.sso.model.User;

@Repository
public class UserDaoJdbc {
    private static final String SQL_USER_VERIFY = "SELECT COUNT(*) FROM permission_operator WHERE operator_login=? AND operator_pwd=SHA1(?)";
    private static final String SQL_USER_GET = "SELECT * FROM permission_operator WHERE operator_login=?";
    private JdbcTemplate jdbcTemplate;
    @Resource
    public void setDataSource(DataSource dataSource){
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    public boolean verifyAccount(String username, String password){
        try{
            //驗證使用者名稱和密碼是否正確
            return 1==this.jdbcTemplate.queryForObject(SQL_USER_VERIFY, new Object[]{username, password}, Integer.class);
        }catch(EmptyResultDataAccessException e){
            return false;
        }
    }
    public User getByUsername(String username){
        try{
            //根據使用者名稱獲取使用者資訊
            return (User)this.jdbcTemplate.queryForObject(SQL_USER_GET, new Object[]{username}, new UserRowMapper());
        }catch(EmptyResultDataAccessException e){
            return new User();
        }
    }
}

class UserRowMapper implements RowMapper<User> {
    @Override
    public User mapRow(ResultSet rs, int index) throws SQLException {
        User user = new User();
        user.setUsercode(rs.getString("operator_code"));
        user.setUsername(rs.getString("operator_login"));
        user.setUsernamePlain(rs.getString("operator_name"));
        return user;
    }
}

下面是用到的User.java

package com.jadyer.sso.model;
public class User {
    private String usercode;
    private String username;
    private String usernamePlain;
    /*-- setter和getter略 --*/
}

下面是deployerConfigContext.xml的修改部分

<!-- 註釋掉預設的attributeRepository實現,採用自定義的UserStubPersonAttributeDao -->
<!--
<bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao"
       p:backingMap-ref="attrRepoBackingMap" />

<util:map id="attrRepoBackingMap">
   <entry key="uid" value="uid" />
   <entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
   <entry key="groupMembership" value="groupMembership" />
</util:map>
-->

下面是WEB-INFviewjspprotocol2.0casServiceValidationSuccess.jsp

<%--

    Licensed to Jasig under one or more contributor license
    agreements. See the NOTICE file distributed with this work
    for additional information regarding copyright ownership.
    Jasig licenses this file to you under the Apache License,
    Version 2.0 (the "License"); you may not use this file
    except in compliance with the License.  You may obtain a
    copy of the License at the following location:

      http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.

--%>
<%@ page session="false" contentType="application/xml; charset=UTF-8" %>
<%@ 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.primaryAuthentication.principal.id)}</cas:user>
        <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>
        <!--
        新增部分如下:CASServer驗證成功後,該頁面負責生成與客戶端互動的XML資訊
        預設的casServiceValidationSuccess.jsp中只包括使用者名稱,並不提供其他的屬性資訊,因此需要對該頁面進行擴充套件
        -->
        <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>
    </cas:authenticationSuccess>
</cas:serviceResponse>

最後是客戶端獲取服務端返回的自定義使用者資訊的index.jsp

<%@ page pageEncoding="UTF-8"%>
<%@ page import="java.util.Map"%>
<%@ page import="java.net.URLDecoder"%>
<%@ page import="org.jasig.cas.client.util.AssertionHolder"%>
<%@ page import="org.jasig.cas.client.authentication.AttributePrincipal"%>

<body style="background-color:#CBE0C9;">
    <span style="color:red; font-size:32px; font-weight:bold;">客戶端登入成功</span>
</body>

<hr size="2">

<%
    AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();
    Map<String, Object> attributes = principal.getAttributes();
    out.print("principal.getName()=" + principal.getName() + "<br/>");
    out.print("request.getRemoteUser()=" + request.getRemoteUser() + "<br/>");
    out.print("登入使用者:" + attributes.get("userId") + "<br/>");
    out.print("登入時間:" + AssertionHolder.getAssertion().getAuthenticationDate() + "<br/>");
    out.print("-----------------------------------------------------------------------<br/>");
    for(Map.Entry<String, Object> entry : attributes.entrySet()){
        //服務端返回中文時需要encode,客戶端接收顯示中文時需要decode,否則會亂碼
        out.print(entry.getKey() + "=" + URLDecoder.decode(entry.getValue().toString(), "UTF-8") + "<br/>");
    }
    out.print("-----------------------------------------------------------------------<br/>");
    Map<String, Object> attributes22 = AssertionHolder.getAssertion().getAttributes();
    for(Map.Entry<String, Object> entry : attributes22.entrySet()){
        out.print(entry.getKey() + "=" + entry.getValue() + "<br/>");
    }
    out.print("-----------------------------------------------------------------------<br/>");
    Map<String, Object> attributes33 = AssertionHolder.getAssertion().getPrincipal().getAttributes();
    for(Map.Entry<String, Object> entry : attributes33.entrySet()){
        out.print(entry.getKey() + "=" + entry.getValue() + "<br/>");
    }
%>

效果圖

關於客戶端如何配置(包括通過spring),詳見本系列的下一篇博文

(今天頭疼還發燒,明天再發客戶端的配置方法)


相關文章