cas sso 单点登陆 登陆及client获取用户信息(三)

来源:互联网 发布:jquery数组push对象 编辑:程序博客网 时间:2024/06/14 05:46

CAS SSO 代码及依赖的jar下载: http://download.csdn.net/detail/nmsbq/9875610

1.导入项目 cas-server-webapp源码导入到idea或eclipse,pom.xml 加入依赖的jar

<!-- https://mvnrepository.com/artifact/org.jasig.cas/cas-server-support-jdbc --><dependency>   <groupId>org.jasig.cas</groupId>   <artifactId>cas-server-support-jdbc</artifactId>   <version>3.5.1</version>   <scope>runtime</scope></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency>   <groupId>mysql</groupId>   <artifactId>mysql-connector-java</artifactId>   <version>5.1.22</version></dependency>

2.CAS服务器深入配置

上面的初体验仅仅是简单的身份验证,实际应用中肯定是要读取数据库的数据,进一步配置CAS服务器怎么读取数据库的信息进行身份验证。 首先打开deployerConfigContext.xml

配置的地方如下:
找到:SimpleTestUsernamePasswordAuthenticationHandler这个验证Handler,这个是比较简单的,只是判断用户名和密码相同即可通过,这个肯定不能在实际应用中使用,弃用注释掉!

<bean

class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
注释掉后在下面添加下面的代码:

<!-- 变更为JDBC验证方式 --><bean id="primaryAuthenticationHandler" class="cn.com.yktour.jdbc.QueryDatabaseAuthenticationHandler"><property name="dataSource" ref="dataSource"></property><property name="sql" value="SELECT login_pwd,id,login_name,nick_name from user where login_name = ?"></property><property name="passwordEncoder" ref="MD5PasswordEncoder"></property></bean>

新的验证实现类 cn.com.yktour.jdbc.QueryDatabaseAuthenticationHandler

根据用户查询用户的SQL SELECT login_pwd,id,login_name,nick_name from table where login_name = ?

注意 你的密码加密方式如果不是简单的MD5 加密,需要重新实现加密方式

在最外层的</bean> 后面添加 数据源配置和MD5加密配置

<!-- 数据源配置 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">   <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>   <property name="url"><value>jdbc:mysql://数据库连接地址:3306/db?characterEncoding=utf8</value></property>   <property name="username"><value>账号</value></property>   <property name="password"><value>密码</value></property></bean><!-- 添加MD5密码加密功能 --><bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">   <constructor-arg index="0">      <value>MD5</value>   </constructor-arg></bean>
QueryDatabaseAuthenticationHandler实现代码

package cn.com.yktour.jdbc;import cn.com.yktour.enity.Member;import cn.com.yktour.util.DefaultPasswordEncoder;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.jasig.cas.authentication.handler.AuthenticationException;import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;import org.springframework.dao.IncorrectResultSizeDataAccessException;import org.springframework.jdbc.core.RowMapper;import org.springside.modules.security.utils.Digests;import org.springside.modules.utils.Encodes;import javax.validation.constraints.NotNull;import java.sql.ResultSet;import java.sql.SQLException;public class QueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {    private final static Log LOG = LogFactory.getLog(QueryDatabaseAuthenticationHandler.class);    @NotNull    private String sql;    @NotNull    private DefaultPasswordEncoder passwordEncoder;    protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) throws AuthenticationException {        LOG.info("=====开始====");        final String username = getPrincipalNameTransformer().transform(credentials.getUsername());        final String password = credentials.getPassword();        try {            LOG.info("==========username:"+username + ",password:"+password);            MemberRowMapper rm = new MemberRowMapper();            final Member member = getJdbcTemplate().queryForObject(this.sql, rm, username);//查询用户对象            String dbPassword = member.getLoginPwd();            String encryptedPassword = passwordEncoder.encode(password);            boolean bl = dbPassword.equals(encryptedPassword);            return bl;        } catch (final IncorrectResultSizeDataAccessException e) {            LOG.error(e.getMessage(),e.fillInStackTrace());            return false;        }    }    /**     * @param sql The sql to set.     */    public void setSql(final String sql) {        this.sql = sql;    }    public void setPasswordEncoder(DefaultPasswordEncoder passwordEncoder) {        this.passwordEncoder = passwordEncoder;    }}


以上完成后把cas项目打成war 改名为 cas.war,复制到 HTTPS comcat 下运行即可

3.配置CAS客户端

在client 客户端 pom.xml添加 jra依赖 

<!-- https://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core --><dependency><groupId>org.jasig.cas.client</groupId><artifactId>cas-client-core</artifactId><version>3.2.2</version></dependency><!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency>

编辑web.xml添加cas相关配置

<!­­-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置 ­­-->    <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>            <param­value>https://cas.test123.com:8443/cas</param­value>        </init­param>        <!­­-- 这里的serverName是服务端的IP ­­-->        <init­param>            <param­name>serverName</param­name>            <param­value>http://client1.test123: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>            <param­value>https://cas.test123.com:8443/cas</param­value>        </init­param>        <init­param>            <param­name>serverName</param­name>            <param­value>http://client1.test123:8080</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>    <!­­-- 该过滤器负责把ticket验证后产生的Assertion放入ThreadLocal中,以便 不能访问web层的资源使用。该过滤器可以使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。比如AssertionHolder.getAssertion().getPrincipal().getName()。 ­­-->    <filter>        <filter­name>CAS Assertion Thread Local Filter</filter­name>        <filter­class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter­class>    </filter>    <filter­mapping>        <filter­name>CAS Assertion Thread Local Filter</filter­name>        <url­pattern>/*</url­pattern>    </filter­mapping>    <!­­ ======================== 单点登录结束 ======================== ­­>

4.cas client 获取用户信息代码

获取用户名

AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();  String loginName = principal.getName(); 


配置属性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 login_pwd,id,login_name,nick_name,real_name,mobile,salt from scm_c_member where {0}"/>        <property name="queryAttributeMapping">            <map>                <!--这里的key需写username,value对应数据库用户名字段-->                <entry key="username" value="login_name"/>            </map>        </property>        <property name="resultAttributeMapping">            <map>                <!--key为对应的数据库字段名称,value为提供给客户端获取的属性名字,系统会自动填充值-->                <entry key="id" value="id"/>                <entry key="mobile" value="mobile"/>                <entry key="login_pwd" value="loginPwd"/>            </map>        </property>    </bean>


配置用户认证凭据转化的解析器

找到credentialsToPrincipalResolvers,为UsernamePasswordCredentialsToPrincipalResolver 注入 attributeRepository,那么attributeRepository 就会被触发并通过此类进行解析,红色为新添部分。

<property name="credentialsToPrincipalResolvers"><list><bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" ><property name="attributeRepository" ref="attributeRepository" /></bean><bean class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" /></list></property>


配置InMemoryServiceRegistryDaoImpl的属性 registeredServices
修改 deployerConfigContext.xml 中的 org.jasig.cas.services.InMemoryServiceRegistryDaoImpl的 属性 registeredServices。修改 registeredServices  的allowedAttributes属性值,将需要在客户端显示的列值加上。

<bean id="serviceRegistryDao"        class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl">       <property name="registeredServices">            <list>                 <bean class="org.jasig.cas.services.RegexRegisteredService">                     <property name="id" value="0" />                     <property name="name" value="HTTP and IMAP" />                     <property name="description" value="Allows HTTP(S) and IMAP(S) protocols" />                     <property name="serviceId" value="^(https?|imaps?)://.*" />                     <property name="evaluationOrder" value="10000001" />                     <!--客户端需要使用的对象的属性名称-->                     <property name="allowedAttributes">                     <list>                         <value>id</value>                         <value>mobile</value>                         <value>loginPwd</value>                     </list>                     </property>                 </bean>           </list>       </property></bean>
提示】网上说此bean中的ignoreAttributes属性默认是不添加用户信息,查看了 CAS 3.5.1版本的 AbstractRegisteredService 源码后,发现其默认值就是false,即:添加属性后,客户端就可见了

配置与客户端交互的xml信息
修改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>

通过完成上面四个步骤的配置后,server端的工作就完成了。cas client获取用户信息:

AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();  Map attributes = principal.getAttributes();  Object moblie=attributes .get("moblie");  









原创粉丝点击