您的位置:首页 > 数据库

cas server 基础 数据库 验证码实现及其配置

2016-07-15 12:27 411 查看
由于需要从业务上将系统拆分成多个独立的子系统,所以需要sso。这里主要是讲述一下server 端的配置.

下面按照几个步骤进行描述

下载cas-server-4.1.9 (https://github.com/apereo/cas)

构建 server (maven)工程。

目录结构

基本配置

验证码配置

1、下载

https://github.com/apereo/cas 可以下载cas-server 的源代码
也可以在直接使用maven 进行构建。
如果是下载源码,那么复制里面的工程(cas-server-webapp)也是一样的


2、构建工程(如果是直接下载源码 跳过这个步骤)

1、创建一个maven 工程, 后面附上 依赖文件。
2、复制从github 下载的 cas-server-webapp 中 src/main/webapp  内容 到 webapp 中,并且将resources 中的文件复制到 resources 中,目的是减少配置。
这两个文件夹中主要是配置文件。不要遗漏。


pom.xml 文件。

<properties>
<spring.version>3.2.6.RELEASE</spring.version>
<cas-server.version>4.1.9</cas-server.version>
</properties>

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

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>

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

<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.22</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>
4000
provided</scope>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.8.RELEASE</version>
</dependency>

<dependency>
<groupId>com.ryantenney.metrics</groupId>
<artifactId>metrics-spring</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>


3、目录结构

resource/services 的json 文件是配置那些服务允许访问cas 的。如果这里没有配置,那么可能会在其他工程跳转到cas 登录的时候出现不被允许的错误。

resouces/messages.properties 国际化配置。

WEB-INF/spring-configuration 一些基础的配置。一般不需要去修改

WEB-INF/view 文件页面存放目录

WEB-INF/webflow 工作流的配置文件

WEB-INF/cas.properties 基础属性的配置文件在有些xml 可以在看${xxxx:yyy} xxxx 就是里面的配置文件

WEB-INF/web.xml

WEB-INF/deployerConfigContext.xml 这个是部署的配置文件,主要修改的也就是这个文件

WEB-INF/cas-servlet.xml cas 主要是使用的spring mvc security webflow , 这个就是springmvc 配置文件

4、基础配置

很多教程都是一来就介绍怎么配置https 这里先不进行ssl 配置,使用http

cookie 配置 默认使用https 的方式,如果使用http 将不会创建全局的cookie

WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml

<!--  TODO 这里将 cookieSecure 修改为了false ,目的是使用http  -->
<bean id="ticketGrantingTicketCookieGenerator"
class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
c:casCookieValueManager-ref="cookieValueManager"
p:cookieSecure="false"
p:cookieMaxAge="-1"
p:cookieName="TGC"
p:cookiePath=""/>


- 国际化的配置  WEB-INF/cas-servlet.xml


<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
p:defaultLocale="zh_CN" />


- 认证处理配置 WEB-INF/deployerConfigContext.xml


<!--  TODO 这里设置 p:requireSecure="false" 目的是为了使用http   -->
<bean id="proxyAuthenticationHandler"
class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:requireSecure="false"
p:httpClient-ref="supportsTrustStoreSslSocketFactoryHttpClient" />


这样http 就配置完成了! 可以进行测试一下了


数据库认证配置

<!--  加密方式配置   -->
<bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
<constructor-arg index="0" value="MD5" />
</bean>

<!-- Oracle connector -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"    />
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:ytym"   />
<property name="username" value="xxx"  />
<property name="password" value="xxx"  />
</bean>

<!-- 使用自定义的查询方式验证用户信息 -->
<bean id="primaryAuthenticationHandler"
class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref="dataSource" />
<property name="passwordEncoder" ref="MD5PasswordEncoder"/>
<property name="sql" value="select login_pass from user where name = ?  " />
</bean>


==QueryDatabaseAuthenticationHandler可以自己实现,根据业务的需要==

3、允许使用cas 认证的服务配置。

默认的配置是用是json 的格式进行配置 resource/services 文件中,但是修改起来难度比较大, 所以这里使用xml 的配置

找到 WEB-INF/deployerConfigContext.xml 中的 serviceRegistryDao 进行修改


<!-- 这里注释掉, 使用xml 的配置 方式 -->
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"
p:registeredServices-ref="registeredServicesList" />

<util:list id="registeredServicesList">
<!--<bean class="org.jasig.cas.services.RegexRegisteredService">-->
<!--<property name="id" value="10000001"/>-->
<!--<property name="name" value="HTTPS and IMAPS"/>-->
<!--<property name="description" value="This service definition authorized all application urls that support HTTPS and IMAPS protocols."/>-->
<!--<property name="serviceId" value="^(https|imaps)://.*"/>-->
<!--<property name="evaluationOrder" value="10000001"/>-->
<!--</bean>-->
<bean class="org.jasig.cas.services.RegexRegisteredService">
<property name="id" value="10000002"/>
<property name="name" value="localhost Test"/>
<property name="description" value="local server config"/>
<property name="serviceId" value="^(http?|https?|imaps?):/
f7fa
/((127\.0\.0\.1)|(localhost)|(test.com))(:[\d]+)?/.*"/>
<property name="evaluationOrder" value="10000002"/>
</bean>
<bean class="org.jasig.cas.services.RegexRegisteredService">
<property name="id" value="10000003"/>
<property name="name" value="ydzx online website"/>
<property name="description" value="ydzx online"/>
<property name="serviceId" value="^(http?|https?|imaps?)://[\w]+.ydzxlm.com/.*"/>
<property name="evaluationOrder" value="10000002"/>
</bean>
</util:list>


5.验证码配置

自己写验证码的实现,并且写好验证码验证的接口

实现可以见后面的代码

1、在 配置验证bean


WEB-INF/cas-servlet.xml
<!-- 验证码验证 验证码超时时间,默认是五分钟 -->
<bean id="authCodeValidateFormAction" class="com.ydzx.cas.server.web.flow.AuthCodeValidateFormAction" />


2、配置登录流程


WEB-INF/webflow/login/login-webflow.xml
<view-state id="viewLoginForm" view="casLoginView" model="credential">
<binder>
<binding property="username" required="true"/>
<binding property="password" required="true"/>
</binder>
<on-entry>
<set name="viewScope.commandName" value="'credential'"/>

<!--
<evaluate expression="samlMetadataUIParserAction" />
-->
</on-entry>
<!--<transition on="submit" bind="true" validate="true" to="realSubmit"/>-->
<!-- 修改,目的是在登录的时候,加上验证码的验证 -->
<transition on="submit" bind="true" validate="true" to="authCodeValidate"/>
</view-state>

<!-- 添加一个新的流程,在登录前添加验证码 -->
<action-state id="authCodeValidate" >
<evaluate expression="authCodeValidateFormAction.validate(flowRequestContext,flowScope.credential, messageContext)" />
<transition on="success" to="realSubmit" />
<transition on="error" to="generateLoginTicket" />
</action-state>


验证码 相关代码

验证码实现类 : VerfiyCode.java
public class VerfiyCode {
private String randomCode;

public VerfiyCode() {
System.setProperty("java.awt.headless", "true");
}

Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色
Random random = new Random();
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}

/**
* 获取图片
*/
public BufferedImage getImage() {
// 在内存中创建图象
int width = 60, height = 20;
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);

// 获取图形上下
Graphics g = image.getGraphics();

// 生成随机
Random random = new Random();

// 设定背景
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);

// 设定字体
g.setFont(new Font("Times New Roman", Font.PLAIN, 18));

// 画边
g.setColor(new Color(255, 255, 255));
g.drawRect(0, 0, width - 1, height - 1);

// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}

// 取随机产生的认证 (4位数 )
String sRand = "";
for (int i = 0; i < 4; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
// 将认证码显示到图象中
g.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生
g.drawString(rand, 13 * i + 6, 16);
}

// 将认证码存入
this.setRandomCode(sRand);
// session.setAttribute("rand",sRand);

// 图象生效
g.dispose();

// 输出图象到页
return image;
}

/**
* 随机数
*/
public String getRandomCode() {
return this.randomCode;
}

public void setRandomCode(String randomCode) {
this.randomCode = randomCode;
}
}


AuthCodeValidateFormAction.java 验证码验证实现
public class AuthCodeValidateFormAction {
protected final Logger logger = LoggerFactory.getLogger(getClass());

private long outTime = 5*60*1000;

public static final String SUCCESS = "success";
public static final String ERROR = "error";

public Event validate(final RequestContext context, final Credential credential,
final MessageContext messageContext) {
try {
HttpServletRequest request = WebUtils.getHttpServletRequest(context);
String image_code = (String) request.getSession().getAttribute("image_no");
Long image_create_time = (Long) request.getSession().getAttribute("image_create_time");

if( (image_create_time != null && Math.abs(System.currentTimeMillis() - image_create_time) > outTime)
|| (image_code == null)   ){
resultMsgWrapper(messageContext,"验证码超时!");
return new Event(this, ERROR);
}

String username = request.getParameter("username");
if (username == null) {
resultMsgWrapper(messageContext,"用户信息不能为空!");
return new Event(this, ERROR);
}

String[] separator_chars = username.split("#@#");

if(separator_chars.length < 2){
resultMsgWrapper(messageContext,"信息不合法!");
return new Event(this, ERROR);
}

if(separator_chars[1] == null && StringUtils.isEmpty(separator_chars[1])){
resultMsgWrapper(messageContext,"验证码不能为空!");
return new Event(this, ERROR);
}

if (image_code != null && image_code.trim().equals(separator_chars[1])) {
request.getSession().removeAttribute("image_no");
} else {
resultMsgWrapper(messageContext,"验证码信息错误!");
return new Event(this, ERROR);
}
return newEvent(SUCCESS);
} catch (final Exception e) {
logger.debug(e.getMessage(), e);
return newEvent(ERROR, e);
}
}

private void resultMsgWrapper(MessageContext messageContext,String msg) {
MessageBuilder msgBuilder = new MessageBuilder();
msgBuilder.defaultText(msg);
messageContext.addMessage(msgBuilder.error().build());
}

/**
* New event based on the given id.
*
* @param id the id
* @return the event
*/
private Event newEvent(final String id) {
return new Event(this, id);
}

/**
* New event based on the id, which contains an error attribute referring to the exception occurred.
*
* @param id    the id
* @param error the error
* @return the event
*/
private Event newEvent(final String id, final Exception error) {
return new Event(this, id, new LocalAttributeMap("error", error));
}

public long getOutTime() {
return outTime;
}

public void setOutTime(long outTime) {
this.outTime = outTime;
}
}


verifyCode.jsp 实现。
<%@ page language="java" contentType="image/jpeg" pageEncoding="UTF-8"
import="VerfiyCode,javax.imageio.ImageIO, javax.servlet.ServletContext,org.springframework.web.context.support.WebApplicationContextUtils,org.springframework.context.ApplicationContext"%>
<%
try {
//设置页面不缓存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.reset();
VerfiyCode verfiyCode = new VerfiyCode();
ImageIO.write(verfiyCode.getImage(), "JPEG", response.getOutputStream());
//存放会话中
session.setAttribute("image_no", verfiyCode.getRandomCode());
session.setAttribute("image_create_time", System.currentTimeMillis());

ServletContext servletContext = config.getServletContext();
//      ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
//      Cache verifyCodeCache = (Cache)applicationContext.getBean("verifyCodeCache");
//      Element element = new Element(verfiyCode.getRandomCode(), "");
//      verifyCodeCache.put(element);

out.clear();
out = pageContext.pushBody();
} catch (Exception e) {
System.out.println(e);
}
%>


login.html 登录页面(WEB-INF/view/jsp/default/ui/casLoginView.jsp)(不能直接使用 只表达基础的意思)

<form action ="login" id="fm1" >
<input type="hidden" name="lt" value="${loginTicket}" />
<input type="hidden" name="execution" value="${flowExecutionKey}" />
<input type="hidden" name="_eventId" value="submit" />
<input class="input1" type="hidden" id="username" path="username" autocomplete="off" htmlEscape="true" />

<table>
<tr>
<td>用户名</td>
<td><input name="name"></td>
</tr>

<tr>
<td>密码</td>
<td><password class="input1" id="password" tabindex="2" path="password" htmlEscape="true" autocomplete="off" /></td>
</tr>

<tr>
<td>验证码</td>
<td>
<input name="name">
<img id="imgValidateCode"   src="<%=request.getContextPath()%>/verifyCode.jsp?rd=1"  />
</td>
</tr>
<Tr>
<Td>  <button onclick="checkUser" >登录</button>
</tr>
</table>
</form>
<script>
function checkUser(){
$("#username").val($  $("#name").val() + "#@#" + $("#image_no").val() );
$("#fm1").submit();
}

</script>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息