如果csdn看着不舒服,请移步http://hi.baidu.com/ae6623/item/34f6e23bb424b8342e0f819f,和本系列教程篇同步更新。
本篇教程cas-server端下载地址:解压后,直接放到tomcat的webapp目录下就能用了,不过你需要登录的话,要修改数据源,C:\tomcat7\webapps\casServer\WEB-INF\deployerConfigContext.xml,嗯。地址:http://pan.baidu.com/share/link?shareid=439449164&uk=436295647
落雨 cas 单点登录
环境:
server端:cas-server-core-3.5.2.jar、cas-client-core-3.2.1.jar
client端:cas-client-core-3.1.3.jar、http屏蔽了https后的casclient.jar(http://blog.csdn.net/dengtaowei/article/details/7039399)
之前做的界面里面缺少一个验证码的功能,上周由于搞其他事情去了,就没有开始验证码的教程写作,今天补上,希望能按照教程制作出你们想要的功能。
结果图:
至此为止,五篇教程,自定义的java类不是很多,还不是很深入。包结构和jar包如下图,程序去我的csdn下载频道下载。
C:\TOMCAT7\WEBAPPS\CASSERVER\WEB-INF\CLASSES\ORG
└─jasig
└─cas
├─authentication
│ └─handler
│ │ CaptchaImageLoginCredentials.class
│ │ Crypt.class
│ │ ImageVaditeAuthenticationViaFormAction.class
│ │ MD5.class
│ │ RsCasDaoAuthenticationHandler.class
│ │
│ ├─captchaImage
│ │ CaptchaImageCreateController.class
│ │
│ └─util
│ ValidatorCodeUtil$ValidatorCode.class
│ ValidatorCodeUtil.class
│
├─util
│ AutowiringSchedulerFactoryBean.class
│
└─web
│ FlowExecutionExceptionResolver.class
│
└─flow
GatewayServicesManagementCheck.class
ServiceAuthorizationCheck.class
1.cas-servlet.xml
.找到C:\tomcat7\webapps\casServer\WEB-INF\cas-servlet.xml文件,在
logoutController
serviceValidateController
legacyValidateController
proxyController
proxyValidateController
samlValidateController
addRegisteredServiceSimpleFormController
editRegisteredServiceSimpleFormController
serviceLogoutViewController
viewStatisticsController
manageRegisteredServicesMultiActionController
openIdProviderController
passThroughController
passThroughController
healthCheckController
captchaImageCreateController
captchaImageCreateController需要我们自己写java类
在上述xml代码后面继续添加一个bean,如下,而类class路径是我们自己写的,在myeclipse里面自己diy的包名org.jasig.cas.authentication.handler.captchaImage.CaptchaImageCreateController
2.然后我们开始写这个CaptchaImageCreateController类
org.jasig.cas.authentication.handler.captchaImage.CaptchaImageCreateController
CaptchaImageCreateController.java
/**
* Project Name:casServerHandler
* File Name:CaptchaImageCreateController.java
* Package Name:org.jasig.cas.authentication.handler.captchaImage
* Date:2013-4-28下午03:04:06
* Copyright (c) 2013, riambsoft All Rights Reserved.
*
*/
package org.jasig.cas.authentication.handler.captchaImage;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.authentication.handler.util.ValidatorCodeUtil;
import org.jasig.cas.authentication.handler.util.ValidatorCodeUtil.ValidatorCode;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
// import com.wokejia.flow.ValidatorCodeUtil.ValidatorCode;
public class CaptchaImageCreateController implements Controller, InitializingBean {
public void afterPropertiesSet() throws Exception {
}
public ModelAndView handleRequest(HttpServletRequest arg0,
HttpServletResponse response) throws Exception {
ValidatorCode codeUtil = ValidatorCodeUtil.getCode();
System.out.println("code="+codeUtil.getCode());
arg0.getSession().setAttribute("code", codeUtil.getCode());
// 禁止图像缓存。
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ServletOutputStream sos = null;
try {
// 将图像输出到Servlet输出流中。
sos = response.getOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(sos);
encoder.encode(codeUtil.getImage());
sos.flush();
sos.close();
} catch (Exception e) {
} finally {
if (null != sos) {
try {
sos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
3.开始搞验证码图片生成java类的编写:
这个类中用到了网上经常用的验证码生成类,这个类你可以自己随便写,下面提供一个网上写的,生成的验证码挺漂亮的。
import org.jasig.cas.authentication.handler.util.ValidatorCodeUtil;
import org.jasig.cas.authentication.handler.util.ValidatorCodeUtil.ValidatorCode;
ValidatorCode类是ValidatorCodeUtil类的一个内部类,代码如下
ValidatorCode.java
/**
* Project Name:casServerHandler
* File Name:ValidatorCodeUtil.java
* Package Name:org.jasig.cas.authentication.handler.util
* Date:2013-4-28下午03:01:38
* Copyright (c) 2013, riambsoft All Rights Reserved.
*
*/
package org.jasig.cas.authentication.handler.util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* ClassName:ValidatorCodeUtil
Function: TODO ADD FUNCTION.
Reason: TODO ADD REASON.
Date: 2013-4-28 下午03:01:38
*
* @author Administrator
* @version
* @since JDK 1.5
* @see
*/
public class ValidatorCodeUtil {
public static ValidatorCode getCode() {
// 验证码图片的宽度。
int width = 80;
// 验证码图片的高度。
int height = 30;
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffImg.createGraphics();
// 创建一个随机数生成器类。
Random random = new Random();
// 设定图像背景色(因为是做背景,所以偏淡)
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
// 创建字体,字体的大小应该根据图片的高度来定。
Font font = new Font("微软雅黑", Font.HANGING_BASELINE, 28);
// 设置字体。
g.setFont(font);
// 画边框。
g.setColor(Color.BLACK);
g.drawRect(0, 0, width - 1, height - 1);
// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到。
// g.setColor(Color.GRAY);
// 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);
// }
// randomCode用于保存随机产生的验证码,以便用户登录后进行验证。
StringBuffer randomCode = new StringBuffer();
// 设置默认生成4个验证码
int length = 4;
// 设置备选验证码:包括"a-z"和数字"0-9"
String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int size = base.length();
// 随机产生4位数字的验证码。
for (int i = 0; i < length; i++) {
// 得到随机产生的验证码数字。
int start = random.nextInt(size);
String strRand = base.substring(start, start + 1);
// 用随机产生的颜色将验证码绘制到图像中。
// 生成随机颜色(因为是做前景,所以偏深)
// g.setColor(getRandColor(1, 100));
// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
g.drawString(strRand, 15 * i + 6, 24);
// 将产生的四个随机数组合在一起。
randomCode.append(strRand);
}
// 图象生效
g.dispose();
ValidatorCode code = new ValidatorCode();
code.image = buffImg;
code.code = randomCode.toString();
return code;
}
// 给定范围获得随机颜色
static 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);
}
/**
*
*
* 验证码图片封装
*
*
* @ClassName: ValidatorCode
* @version V1.0
* @date 2012-4-9 下午07:24:14
* @author 罗伟俊
*
*/
public static class ValidatorCode {
private BufferedImage image;
private String code;
/**
*
* 图片流
*
*
* @return
*/
public BufferedImage getImage() {
return image;
}
/**
*
* 验证码
*
*
* @return
*/
public String getCode() {
return code;
}
}
}
4.验证码生成做完了,那么我们开始修改默认的认证器的认证方式,增加验证码验证
自己新建一个java类:
ImageVaditeAuthenticationViaFormAction.java,此类其实是改造的org.jasig.cas.web.flow.AuthenticationViaFormAction.java这个类,它里面本来只是验证用户名和密码,我们增加一个成员变量,code,然后在验证用户名和密码之前,我们先开始验证验证码。
/*
* 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.
*/
package org.jasig.cas.authentication.handler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.ticket.TicketException;
import org.jasig.cas.web.bind.CredentialsBinder;
import org.jasig.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.util.StringUtils;
import org.springframework.web.util.CookieGenerator;
import org.springframework.webflow.execution.RequestContext;
/**
* Action to authenticate credentials and retrieve a TicketGrantingTicket for those credentials. If there is a request for renew, then it also generates the Service Ticket required.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0.4
*/
public class ImageVaditeAuthenticationViaFormAction {
// 验证码参数:
private String code = "code";
/**
* Binder that allows additional binding of form object beyond Spring defaults.
*/
private CredentialsBinder credentialsBinder;
/** Core we delegate to for handling all ticket related tasks. */
@NotNull
private CentralAuthenticationService centralAuthenticationService;
@NotNull
private CookieGenerator warnCookieGenerator;
protected Logger logger = LoggerFactory.getLogger(getClass());
public final void doBind(final RequestContext context, final Credentials credentials) throws Exception {
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
if (this.credentialsBinder != null && this.credentialsBinder.supports(credentials.getClass())) {
this.credentialsBinder.bind(request, credentials);
}
}
public final String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception {
// 检测验证码
if (credentials instanceof CaptchaImageLoginCredentials) {
// 这个类也是我们自己搞的,里面能取到验证码
CaptchaImageLoginCredentials rmupc = (CaptchaImageLoginCredentials) credentials;
// 从session中取出生成验证码的时候就保存在session中的验证码
String sessionCode = (String) WebUtils.getHttpServletRequest(context).getSession().getAttribute(code);
// 如果验证码为null
if (rmupc.getCode() == null) {
// 写入日志
logger.warn("验证码为空");
// 错误信息,会在配置文件(messages_zh_CN.properties)里面先定义好
final String code = "login.code.tip";
// 发送错误信息到前台
messageContext.addMessage(new MessageBuilder().error().code(code).arg("").defaultText(code).build());
return "error";
}
// 如果验证码不正确
if (!rmupc.getCode().toUpperCase().equals(sessionCode.toUpperCase())) {
logger.warn("验证码检验有误");
final String code = "login.code.error";
messageContext.addMessage(new MessageBuilder().error().code(code).arg("").defaultText(code).build());
return "error";
}
}
// Validate login ticket
final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context);
final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context);
if (!authoritativeLoginTicket.equals(providedLoginTicket)) {
this.logger.warn("Invalid login ticket " + providedLoginTicket);
final String code = "INVALID_TICKET";
messageContext.addMessage(new MessageBuilder().error().code(code).arg(providedLoginTicket).defaultText(code).build());
return "error";
}
final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);
final Service service = WebUtils.getService(context);
if (StringUtils.hasText(context.getRequestParameters().get("renew")) && ticketGrantingTicketId != null && service != null) {
try {
final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service, credentials);
WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
putWarnCookieIfRequestParameterPresent(context);
return "warn";
} catch (final TicketException e) {
if (isCauseAuthenticationException(e)) {
populateErrorsInstance(e, messageContext);
return getAuthenticationExceptionEventId(e);
}
this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);
if (logger.isDebugEnabled()) {
logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e);
}
}
}
try {
WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials));
putWarnCookieIfRequestParameterPresent(context);
return "success";
} catch (final TicketException e) {
populateErrorsInstance(e, messageContext);
if (isCauseAuthenticationException(e))
return getAuthenticationExceptionEventId(e);
return "error";
}
}
private void populateErrorsInstance(final TicketException e, final MessageContext messageContext) {
try {
messageContext.addMessage(new MessageBuilder().error().code(e.getCode()).defaultText(e.getCode()).build());
} catch (final Exception fe) {
logger.error(fe.getMessage(), fe);
}
}
private void putWarnCookieIfRequestParameterPresent(final RequestContext context) {
final HttpServletResponse response = WebUtils.getHttpServletResponse(context);
if (StringUtils.hasText(context.getExternalContext().getRequestParameterMap().get("warn"))) {
this.warnCookieGenerator.addCookie(response, "true");
} else {
this.warnCookieGenerator.removeCookie(response);
}
}
private AuthenticationException getAuthenticationExceptionAsCause(final TicketException e) {
return (AuthenticationException) e.getCause();
}
private String getAuthenticationExceptionEventId(final TicketException e) {
final AuthenticationException authEx = getAuthenticationExceptionAsCause(e);
if (this.logger.isDebugEnabled())
this.logger.debug("An authentication error has occurred. Returning the event id " + authEx.getType());
return authEx.getType();
}
private boolean isCauseAuthenticationException(final TicketException e) {
return e.getCause() != null && AuthenticationException.class.isAssignableFrom(e.getCause().getClass());
}
public final void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) {
this.centralAuthenticationService = centralAuthenticationService;
}
/**
* Set a CredentialsBinder for additional binding of the HttpServletRequest to the Credentials instance, beyond our default binding of the Credentials as a Form Object in Spring WebMVC parlance. By the time we invoke this CredentialsBinder, we have already engaged in default binding such that for each HttpServletRequest parameter, if there was a JavaBean property of the Credentials implementation of the same name, we have set that property to be the value of the corresponding request parameter. This CredentialsBinder plugin point exists to allow consideration of things other than HttpServletRequest parameters in populating the Credentials (or more sophisticated consideration of the HttpServletRequest parameters).
*
* @param credentialsBinder
* the credentials binder to set.
*/
public final void setCredentialsBinder(final CredentialsBinder credentialsBinder) {
this.credentialsBinder = credentialsBinder;
}
public final void setWarnCookieGenerator(final CookieGenerator warnCookieGenerator) {
this.warnCookieGenerator = warnCookieGenerator;
}
}
CaptchaImageLoginCredentials.java
/**
* Project Name:casServerHandler
* File Name:CaptchaImageLoginCredentials.java
* Package Name:org.jasig.cas.authentication.handler
* Date:2013-4-28下午06:55:08
* Copyright (c) 2013, riambsoft All Rights Reserved.
*
*/
package org.jasig.cas.authentication.handler;
import java.util.Map;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.jasig.cas.authentication.principal.RememberMeUsernamePasswordCredentials;
/**
* ClassName:CaptchaImageLoginCredentials
Function: TODO ADD FUNCTION.
Reason: TODO ADD REASON.
Date: 2013-4-28 下午06:55:08
*
* @author Administrator
* @version
* @since JDK 1.5
* @see
*/
public class CaptchaImageLoginCredentials extends RememberMeUsernamePasswordCredentials {
private static final long serialVersionUID = 1L;
private Map param;
/** The username. */
@NotNull
@Size(min = 1, message = "验证码为空")
private String code;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Map getParam() {
return param;
}
public void setParam(Map param) {
this.param = param;
}
}
5.下面开始对xml文件进行修改:
1)还是刚才那个路径,我们之前修改的xml文件:C:\tomcat7\webapps\casServer\WEB-INF\cas-servlet.xml
找到节点:viewLoginForm
其实你看到就是增加一个binding属性code,方便注入到ImageVaditeAuthenticationViaFormAction这个类里面。
文章末尾会粘贴所有xml文件,以及源码下载地址
3)在C:\tomcat7\webapps\casServer\WEB-INF\classes最下面添加错误提示信息,由于是使用unicode编码,所以是看不懂的字符
login.code.tip=\u8BF7\u8F93\u5165\u9A8C\u8BC1\u7801
login.code.error=\u9A8C\u8BC1\u7801\u8F93\u5165\u6709\u8BEF
其实这两行的意思如下:
login.code.tip=请输入验证码
login.code.error=验证码输入有误
4)C:\tomcat7\webapps\casServer\WEB-INF\web.xml下新增对URL的处理
cas
/captcha.htm
这一步很重要,否则首页打开时,根本找不到captacha.htm而不显示图片,我做这个的时候,也是搞了好久,才发现web.xml这么重要的配置忘记写。
5)现在去找你的前台页面C:\tomcat7\webapps\casServer\WEB-INF\view\jsp\default\ui\casLoginView.jsp,给加上验证码的文本框和响应事件吧,比如说看不清楚换一张
ok 大功告成,编译完的class这几个文件,统统放入你的server端的各个指定路径下,重启tomcat,回帖感谢各位百度前辈吧。
文中所涉及的xml文件完整版:
下载地址:http://pan.baidu.com/share/link?shareid=439449164&uk=436295647
由于是先搞完的程序,后写的文档,所有如有步骤遗漏请及时联系本人,定做详细解答
2013年5月13日10:59:48
落雨
qq:394263788
csdn博客:落雨 :http://blog.csdn.net/ae6623
百度博客:落雨 :http://hi.baidu.com/ae6623/item/34f6e23bb424b8342e0f819f
http://hi.baidu.com/ae6623
参考帖子:http://my.oschina.net/wjgood/blog/90407