您的位置:首页 > 其它

4、CAS单点登录源码解析之【用户认证】

2016-01-23 17:43 447 查看


前期准备

已经搭建好了集成了CAS客户端的应用系统和CAS服务器

1.应用系统webapp(http://127.0.0.1:8090/webapp/main.do)

2.CAS单点登录服务器端(http://127.0.0.1:8081/cas-server/)

本次讨论包括CAS单点登录服务器端的部分源码,以及在此基础上进行用户认证二次开发,因此需要修改部分CAS服务器端的源码,源码部分的修改在下面进行讨论。关于CAS客户端、CAS服务器端和CAS单点登出的源码分析,请参考另外三篇文章

CAS客户端:/article/11163792.html

CAS服务器端:/article/11163793.html

CAS单点登出:/article/11163777.html

deployerConfigContext.xml部分代码

[html] view
plaincopy

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

<property name="driverClassName" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://127.0.0.1:3306/MASTER?useUnicode=true&characterEncoding=UTF-8" />

<property name="username" value="root" />

<property name="password" value="root" />

<property name="initialSize" value="5"/>

<property name="maxActive" value="10" />

<property name="maxIdle" value="100" />

<property name="maxWait" value="1000" />

<property name="timeBetweenEvictionRunsMillis" value="1000"/>

<property name="testWhileIdle" value="true"/>

<property name="validationQuery" value="select 1"/>

</bean>

CAS服务器端的用户认证,默认采用SimpleTestUsernamePasswordAuthenticationHandler类,当输入的用户名和密码相同时,视为认证通过。但实际情况我们会根据需求需要增加自己的用户认证功能,首先在deployerConfigContext.xml配置文件中增加数据源的配置信息(mysql)。

deployerConfigContext.xml部分代码

[html] view
plaincopy

<bean id="authenticationManager" class="org.jasig.cas.authentication.AuthenticationManagerImpl">

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

<property name="authenticationHandlers">

<list>

<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"

p:httpClient-ref="httpClient" />

<!-- 注解默认的认证方式 -->

<!--bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler"/-->

<!-- 此处为增加部分 start -->

<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">

<property name="sql" value="SELECT PASSWORD FROM USER"/>

<!-- 引用数据源 -->

<property name="dataSource" ref="dataSource" />

<!-- 定义MD5的加密方式 -->

<property name="passwordEncoder">

<bean class="org.jasig.cas.authentication.handler.ext.MD5PasswordEncoder"></bean>

</property>

</bean>

<!-- 此处为增加部分 end -->

</list>

</property>

</bean>

<!-- 注解默认的属性

<bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao">

<property name="backingMap">

<map>

<entry key="uid" value="uid" />

<entry key="eduPersonAffiliation" value="eduPersonAffiliation" />

<entry key="groupMembership" value="groupMembership" />

</map>

</property>

</bean>

-->

<!-- 此处为增加部分 start -->

<bean class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao" id="attributeRepository">

<constructor-arg index="0" ref="dataSource"/>

<constructor-arg index="1" value="SELECT * FROM USER WHERE {0}"/>

<property name="queryAttributeMapping">

<map>

<!-- key对应登录信息, vlaue对应数据库字段 -->

<entry key="username" value="LOGIN_NAME"/>

</map>

</property>

<property name="resultAttributeMapping">

<map>

<!-- key对应数据库字段 value对应attribute中的key -->

<entry key="mobile" value="mobile"/>

<entry key="email" value="email"/>

</map>

</property>

</bean>

<!-- 此处为增加部分 end -->

<bean id="serviceRegistryDao" class="com.uws.uaserver.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" />

</bean>

<bean class="org.jasig.cas.services.RegexRegisteredService">

<property name="id" value="1" />

<property name="name" value="HTTP and IMAP on example.com" />

<property name="description" value="Allows HTTP(S) and IMAP(S) protocols on example.com" />

<property name="serviceId" value="^(https?|imaps?)://([A-Za-z0-9_-]+\.)*example\.com/.*" />

<property name="evaluationOrder" value="0" />

</bean>

</list>

-->

</property>

</bean>

/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp

[html] view
plaincopy

<%@ page session="false" %>

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

<!-- 增加部分 start -->

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

<!-- 增加部分 end -->

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

修改deployerConfigContext.xml配置文件,注解默认的认证配置和默认的attributeRepository配置信息,增加用户自己的认证配置和attributeRepository配置。

初始化QueryDatabaseAuthenticationHandler类的sql属性(SELECT PASSWORD FROM USER),引用数据源dataSource(mysql数据源),初始化passwordEncode属性(MD5PasswordEncoderr密码加密类)。此处的sql属性只是一个简单的查询语句,实际应用中可以定义一个复杂的SQL语句。
增加自己的attributeRepository配置。dataScore和SELECT * FROM USER WHERE {0}为SingleRowJdbcPersonAttributeDao构造方法的两个参数,queryAttributeMapping是为了组装SQL(SELECT * FROM USER WHERE LOGIN_NAME=username),resultAttributeMapping是SQL查询返回结果属性。

修改casServiceValidationSuccess.jsp页面,由于默认的页面只有user的信息,并没有attributes的信息,因此我们需要增加该信息,本文中只增加了mobile和email,实际应用中可根据需要增加多个属性信息。

该页面最终的返回结果如下:

[html] view
plaincopy

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>

<cas:authenticationSuccess>

<cas:user>system</cas:user>

<cas:attributes>

<cas:mobile>13688888888</cas:mobile>

<cas:email>xxx@master.com</cas:email>

</cas:attributes>

</cas:authenticationSuccess>

</cas:serviceResponse>

客户端获取用户认证的信息的代码

[java] view
plaincopy

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();

String loginName = principal.getName();

Map<String, Object> attributes = principal.getAttributes();

if (attributes != null) {

String mobile = attributes.get("mobile"));

String email = attributes.get("email"));

}

QueryDatabaseAuthenticationHandler的authenticateUsernamePasswordInternal方法

[java] view
plaincopy

protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials)

throws AuthenticationException {

//用户名

final String username = getPrincipalNameTransformer().transform(credentials.getUsername());

//密码

final String password = credentials.getPassword();

Map resultMap = null;

try {

resultMap = getJdbcTemplate().queryForMap(this.sql, new Object[] { username });

String dbPassword = (resultMap.get("PASSWORD") == null) ? ""

: resultMap.get("PASSWORD").toString();

String encryptedPassword = getPasswordEncoder().encode(password);

//判断密码是否相等

return dbPassword.equals(encryptedPassword);

} catch (IncorrectResultSizeDataAccessException e) {

}

return false;

}

QueryDatabaseAuthenticationHandler的authenticateUsernamePasswordInternal方法,要做的就是获取用户输入的用户名和密码,用配置文件中配置的加密方式(MD5PasswordEncoder类)进行加密,用加密后的密码encryptedPassword和用SQL语句在数据库中查询的密码(dbPassword)进行对比,并返回结果。

MD5PasswordEncoder类

[java] view
plaincopy

public class MD5PasswordEncoder implements PasswordEncoder {

public String encode(String password) {

try {

return MD5.crypt(password);//MD5加密

} catch (NoSuchAlgorithmException e) {

e.printStackTrace();

}

return null;

}

}

MD5PasswordEncoder类,要做的就是对用户输入的密码进行加密。此类是新增加的类,用户可以增加任何有加密逻辑的类,但此类必须实现org.jasig.cas.authentication.handler.PasswordEncoder接口,并实现String encode(String password)方法。

至此,CAS用户认证的修改已经完成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: