cas的更多配置

发表时间:2018-04-02 10:15:55 浏览量( 68 ) 留言数( 0 )

学习目标:

1、了解cas的更多配置


学习过程:

   上一节课我们已经下载了源代码了。因为4.3以后的版本使用的gradle编译的,所以这里我使用了4.0.x的版本,使用eclipse打开后,当然也就是删除一大推的jar包。然后有一个小地方修改一下,否则会有个警告的,改成这个样子:

 <aspectj.version>1.6.11</aspectj.version>

然后使用mvn install进行编译,注意不要跳过测试。否则也会报错的。

我们之前使用的war就在这个下面:

attcontent/6c2d54ea-57fb-43aa-9714-3614deb36a5c.png

一、账号密码通过数据库读取

1、建立账号表。修改数据库语句

    显然这样的账号是不行的,一般我们的账号密码信息都是保存在数据库中的。我们可以先建立这样的表。

CREATE TABLE `sys_user` (

  `user_id` varchar(32) NOT NULL DEFAULT '',

  `login_name` varchar(100) DEFAULT NULL,

  `login_pass` varchar(100) DEFAULT NULL,

  `salt` varchar(100) DEFAULT NULL,

  PRIMARY KEY (`user_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    cas已经有一个基本的实现类QueryDatabaseAuthenticationHandler可以实现这个逻辑了。这个逻辑比较简单,就是从数据库里面查询密码,然后匹配密码就可以了。代码如下:

public class QueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {

    @NotNull
    private String sql;

    /** {@inheritDoc} */
    @Override
    protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential)
            throws GeneralSecurityException, PreventedException {

        final String username = credential.getUsername();
        final String encryptedPassword = this.getPasswordEncoder().encode(credential.getPassword());
        try {
            final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username);
            if (!dbPassword.equals(encryptedPassword)) {
                throw new FailedLoginException("Password does not match value on record.");
            }
        } 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(credential, new SimplePrincipal(username), null);
    }

    /**
     * @param sql The sql to set.
     */
    public void setSql(final String sql) {
        this.sql = sql;
    }
}

   由上面的代码可知,我们需要的就是注入sql查询语句和一个DataSource,这个sql需要根据用户名查询对应的密码,你可以根据自己的表结构进行查询。我们还需要配置一个DataSource,并且导入相关的包。这些对我们来说应该都不成问题的了。

修改pom.xml,导入mysql的驱动包

	<dependency>
		<groupId>org.jasig.cas</groupId>
		<artifactId>cas-server-support-jdbc</artifactId>
		<version>${project.version}</version>
	</dependency>

	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.35</version>
	</dependency>

修改deployerConfigContext.xml文件,加入dataSource和QueryDatabaseAuthenticationHandler的配置,

先注释了原来默认的账号密码的配置

 <!--     
    <bean id="primaryAuthenticationHandler"
          class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
        <property name="users">
            <map>
                <entry key="casuser" value="Mellon"/>
            </map>
        </property>
    </bean>
 -->


然后配置QueryDatabaseAuthenticationHandler和dataSource

        <bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
	  <property name="dataSource" ref="dataSource" />
	  <property name="sql" value="select login_pass from sys_user where login_name=?" />
	</bean>
	<bean id="dataSource"
		class=" org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver">
		</property>
		<property name="url"
			value="jdbc:mysql://localhost:3306/mytest?useUnicode=true&amp;characterEncoding=utf8">
		</property>
		<property name="username" value="root"></property>
		<property name="password" value="123456"></property>
	</bean>

测试一下

重新打包。把cas.war复制到webapps中。然后启动tomcat。就可以通过数据库的账号和密码等信息进行登陆了。


二、实现自己的登陆页面

登陆页面你也可以改成你们自己公司单位需要的。

1. CAS默认登录页面

   cas统一认证的登陆页面位于:cas目录/WEB-INF/view/jsp/default 文件夹里。复制一个新的页面管理页面,复制一份default文件夹重命名为myview。

2、 修改页面引用default_views.properties,再src/main/resources中打开这个文件,搜索default,全部换成myview,下面是部分截图。

attcontent/eb80474a-f327-424b-93a0-6c07ece82d91.png


3. 修改cas.properties

打开cas.properties

attcontent/147637fc-1084-490a-ab24-30cf8486d741.png

    修改 cas.viewResolver.basename=myview 

    这样我们只是将登陆页面拷贝了一份然后指向这份拷贝。一会我们只需要修改myview下面的页面就可以了。


4. 修改casLoginView.jsp页面

   casLoginView.jsp是cas默认的登录页面。我们可以通过修改这个Jsp文件来完成自定义登录页面。 页面相对比较简单,你可以配合公司的美工改一下就可以了。国际化文件也改成中文的。其中CAS登录验错误信息、username信息、password信息、CAS验证用户登录hidden信息都是必须的。你可以添加和删除其它信息。



三、添加验证码功能

   cas有使用Spring Web Flow框架,注意我们现在需要修改cas的源代码,cas的源代码每一个类都需要标识协议的。

/*
 * 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.
 */

   其登陆的主要逻辑在这里配置:login-webflow.xml这里配置了一个实体对象就是登陆的Bean对象

<var name="credential" class="org.jasig.cas.authentication.UsernamePasswordCredential" />

登陆的节点定义在这里

	<view-state id="viewLoginForm" view="casLoginView" model="credential">
        <binder>
            <binding property="username" />
            <binding property="password" />
        </binder>
        <on-entry>
            <set name="viewScope.commandName" value="'credential'" />
        </on-entry>
		<transition on="submit" bind="true" validate="true" to="realSubmit">
            <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credential)" />
        </transition>
	</view-state>

   我们主要就是要修改这些地方了。其中binder定义了绑定属性,authenticationViaFormAction.doBind方法提供了绑定的功能。

1、扩展UsernamePasswordCredential类,添加一个code验证码属性

/**
 * Credential for authenticating with a username and password and code.
 *
 * @author liubao
 * @author liubao
 * @since 3.0
 */
public class UsernamePasswordCredentialCode extends UsernamePasswordCredential{


	/** Unique ID for serialization. */
	private static final long serialVersionUID = 700605081472810993L;
	
	 /** The code. */
	@NotNull
    @Size(min=1,message = "required.verification.code")
    private String code;

    /**
     * @return Returns the code.
     */
	public String getCode() {
		return code;
	}

    /**
     * @param password The code to set.
     */
	public void setCode(String code) {
		this.code = code;
	}
}


2、修改login-webflow.xml,定义上面的新的UsernamePasswordCredentialCode类,已经和页面的绑定属性

   <!--  <var name="credential" class="org.jasig.cas.authentication.UsernamePasswordCredential" /> -->
    <!-- 改成自定义的对象 -->
     <var name="credential" class="org.jasig.cas.authentication.UsernamePasswordCredentialCode" />

修改绑定

        <binder>
            <binding property="username" />
            <binding property="password" />
            
             <!-- 添加的验证码属性 -->
            <binding property="code" />
            
        </binder>


3、添加验证节点,注意看,原来的submit直接就跳去realSubmit节点,现在新添加了一个validatorCode节点。submit先去validatorCode节点,改节点回调用authenticationViaFormAction.validatorCode,这个方法我们稍后就定义了。

	<view-state id="viewLoginForm" view="casLoginView" model="credential">
        <binder>
            <binding property="username" />
            <binding property="password" />
            
             <!-- 添加的验证码属性 -->
            <binding property="code" />
            
        </binder>
        <on-entry>
            <set name="viewScope.commandName" value="'credential'" />
        </on-entry>
		<transition on="submit" bind="true" validate="true" to="validatorCode">
            <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credential)" />
        </transition>
	</view-state>
	
	
	<!-- 添加 validatorCode节点,提供验证码的验证功能,成功后跳去提交节点,失败返回 -->
    <action-state id="validatorCode">
        <evaluate expression="authenticationViaFormAction.validatorCode(flowRequestContext, flowScope.credential, messageContext)"></evaluate>
        <transition on="error" to="generateLoginTicket" />
        <transition on="success" to="realSubmit" />
    </action-state>


4、验证方法,打开authenticationViaFormAction类,添加validatorCode方法,逻辑就非常简单,这里就不多说了。定义如下:

	public final String validatorCode(final RequestContext context, final Credential credential,
			final MessageContext messageContext) throws Exception {
		final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
		HttpSession session = request.getSession();
		String code = (String) session.getAttribute("vercode");
		session.removeAttribute("vercode");

		System.out.println("VerCode======");
		
		UsernamePasswordCredentialCode upc = (UsernamePasswordCredentialCode) credential;
		String submitCode = upc.getCode();

		
		System.out.println("vercode=="+code+",submitCode="+submitCode);
		
		if (StringUtils.isEmpty(submitCode) || StringUtils.isEmpty(code)) {
			messageContext.addMessage(new MessageBuilder().code("required.verification.code").build());
			System.out.println("vercode==null");
			return "error";
		}
		if (!submitCode.equalsIgnoreCase(code)) {
			System.out.println("vercode==not equals==");
			messageContext.addMessage(new MessageBuilder().code("error.verification.code").build());
			return "error";
		}
		
		return "success";
	}


5、本我们之前项目的验证码的实现类拷贝过来。

attcontent/7db42d7f-ab75-4915-b624-4d19ee6d4307.png

定义对应的spring配置文件codeContext.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
   

    <!-- The scheduler bean wires up any triggers that define scheduled tasks -->
    <bean id="verifyCodeControl" class="com.controller.VerifyCodeControl"/>
            
</beans>

同时配置web.xml

  <servlet>
		<servlet-name>springServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>
			       /WEB-INF/spring-configuration/codeContext.xml
            </param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>springServlet</servlet-name>
		<url-pattern>/cerCode.jpg</url-pattern>
	</servlet-mapping>


6、修改登陆页面casLoginView.jsp

     <section class="row">
      <label for="code"><spring:message code="screen.welcome.label.code" /></label>
      <spring:message code="screen.welcome.label.code.accesskey" var="codeAccessKey" />
      <form:input cssClass="required" cssErrorClass="error" id="code" size="15" tabindex="2" path="code"  accesskey="${codeAccessKey}" htmlEscape="true" autocomplete="off" />
      <img id="randomcode_img" src="cerCode.jpg"align='absMiddle' />
    </section>


7、添加对应的国际化配置文件

required.verification.code=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
error.verification.code=\u9A8C\u8BC1\u7801\u9519\u8BEF
screen.welcome.label.code=\u9A8C\u8BC1\u7801
screen.welcome.label.code.accesskey=c

8、修改完成后需要重新编译打包  ,注意跳过一下checkStyle。

编译cas 4.0.4时候需要注意两个问题:

1、使用JDK1.7.

2、 install -Dcheckstyle.skip=true


四、自定义QueryDatabaseAuthenticationHandler以实现加盐和密码加密

前面我们使用了QueryDatabaseAuthenticationHandler实现了对数据库的账号密码进行认证的功能,但是一般数据库的密码都是加密的。所以直接使用QueryDatabaseAuthenticationHandler在某些公司可能不好处理。所以我们需要定义一个自己的QueryDatabaseAuthenticationHandler,你可以仿照QueryDatabaseAuthenticationHandler来设计一个就可以了。

自定义一个验证器

public class QueryDatabaseEncryptAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {

    @NotNull
    private String sql;

    /** {@inheritDoc} */
    @Override
    protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential)
            throws GeneralSecurityException, PreventedException {

        final String username = credential.getUsername();
        String encryptedPassword = this.getPasswordEncoder().encode(credential.getPassword());
        try {
        	
            //数据库的密码和盐
            final Map<String, Object> values = getJdbcTemplate().queryForMap(this.sql, username);
            String dbPassword = (String) values.get("login_pass");
            String salt = (String) values.get("salt");
            
            //对用户输入的密码加密和加盐后再进行匹配
            encryptedPassword=  new Md5Hash(encryptedPassword, salt, 1).toString();
            
            if (!dbPassword.equals(encryptedPassword)) {
                throw new FailedLoginException("Password does not match value on record.");
            }
        } 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(credential, new SimplePrincipal(username), null);
    }

    /**
     * @param sql The sql to set.
     */
    public void setSql(final String sql) {
        this.sql = sql;
    }
}


修改deployerConfigContext.xml文件

     <bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseEncryptAuthenticationHandler">
	  <property name="dataSource" ref="dataSource" />
	  <property name="sql" value="select login_pass,slat from sys_user where login_name=?" />
	</bean>