就像我们看到的各个大的网站,用户在登录时默认情况下是不出现验证码的,如果用户连续错误输入3次,就将出现验证码,这样做的优势,此处不再赘述。下面开始详细的配置
一、软件环境
1、cas-client:cas-client-3.2.1-release
2、cas-server:cas-server-3.5.2-release
二、验证码配置及验证
1、既然要使用验证码,那么得有生成验证码的程序,验证码的程序在网上一搜一大把的。
此处提供一个示例
package org.wy.captcha; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.util.Random; 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); } /** * * <p class="detail"> * 验证码图片封装 * </p> * * @ClassName: ValidatorCode * @version V1.0 * @date 2012-4-9 下午07:24:14 * @author 罗伟俊 * */ public static class ValidatorCode { private BufferedImage image; private String code; /** * <p class="detail"> * 图片流 * </p> * * @return */ public BufferedImage getImage() { return image; } /** * <p class="detail"> * 验证码 * </p> * * @return */ public String getCode() { return code; } } }
2、验证码已经生成,那么怎么将验证码推送到登录页面
package org.wy.captcha; import java.io.IOException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import org.wy.captcha.ValidatorCodeUtil.ValidatorCode; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; public class CaptchaImageCreateController implements Controller, InitializingBean { private Logger log = LoggerFactory.getLogger(CaptchaImageCreateController.class); public void afterPropertiesSet() throws Exception { } public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ValidatorCode codeUtil = ValidatorCodeUtil.getCode(); log.info("验证码code==================" + codeUtil.getCode()); request.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) { e.printStackTrace(); } finally { if (null != sos) { try { sos.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } }
登录页面接受验证码,login.jsp
<!-- 验证码 --> <div> <label for="code" class="fl-label">验证码:</label> <input class="required" type="text" tabindex="3" id="code" size="10" name="code" autocomplete="off" /> <div style="height:30px;width:150px;text-align:center;margin-left:15px; vertical-align:middle; display: table-cell;"> <a href="javascript:refresh();" onclick="refresh();" style="width:130px ;height:30px;"> <span style="display: block; float:left; width:60px; height:25px; float:left;"> <img id="vali" width="60" height="30" src="captcha.htm" /> </span> <span style="display:block;width:60px;height:100%;float:left;vertical-align:middle; display: table-cell;margin-left:15px;">看不清楚?换一个</span> </a> </div> </div>
那么现在有创建和接受的了,那么怎么将这两者联系起来哪?我们看下cas-servlet.xml
在文件中添加
<bean id="handlerMappingC" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/logout">logoutController</prop> <prop key="/serviceValidate">serviceValidateController</prop> <prop key="/validate">legacyValidateController</prop> <prop key="/proxy">proxyController</prop> <prop key="/proxyValidate">proxyValidateController</prop> <prop key="/samlValidate">samlValidateController</prop> <prop key="/services/add.html">addRegisteredServiceSimpleFormController</prop> <prop key="/services/edit.html">editRegisteredServiceSimpleFormController</prop> <prop key="/services/loggedOut.html">serviceLogoutViewController</prop> <prop key="/services/viewStatistics.html">viewStatisticsController</prop> <prop key="/services/*">manageRegisteredServicesMultiActionController</prop> <prop key="/openid/*">openIdProviderController</prop> <prop key="/authorizationFailure.html">passThroughController</prop> <prop key="/403.html">passThroughController</prop> <prop key="/status">healthCheckController</prop> </props> </property>
添加如下代码:
<prop key="/captcha.htm">captchaImageCreateController</prop>
<!-- 验证码处理类 --> <bean id="captchaImageCreateController" class="org.wy.captcha.CaptchaImageCreateController"></bean>
3、修改验证流程中处理类
我们打开login-webflow.xml,看到
<var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />
只有检验用户名和密码的,我们现在要校验验证码,就要扩张此类
package org.wy.captcha; import java.util.Map; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.jasig.cas.authentication.principal.RememberMeUsernamePasswordCredentials; public class CaptchaImageLoginCredentials extends RememberMeUsernamePasswordCredentials { private static final long serialVersionUID = 1L; private Map<String, Object> param; //验证码 @NotNull @Size(min = 1, message = "login.code.tip")//验证码为空 private String code; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Map<String, Object> getParam() { return param; } public void setParam(Map<String, Object> param) { this.param = param; } }
并用此类替换login-webfolw.xml
<var name="credentials" class="org.wy.captcha.CaptchaImageLoginCredentials" />
在再login-webflow.xml中找到
<view-state id="viewLoginForm" view="casLoginView" model="credentials"> <binder> <binding property="username" /> <binding property="password" /> </binder> <on-entry> <set name="viewScope.commandName" value="'credentials'" /> </on-entry> <transition on="submit" bind="true" validate="true" to="realSubmit"> <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" /> </transition> </view-state>
在<binder>中添加验证码属性
<binding property="code" />
4、修该验证码校验处理类
在cas-servlet.xml找到
<bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.AuthenticationViaFormAction" p:centralAuthenticationService-ref="centralAuthenticationService" p:warnCookieGenerator-ref="warnCookieGenerator"/>
我们在AuthenticationViaFormAction中看到验证登录的流程,验证码的校验也要在此处进行,扩张此类
/* * 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.wy.captcha; 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; } }
然后将xml中的配置替换成扩展后的类,如下
<bean id="authenticationViaFormAction" class="org.wy.captcha.ImageVaditeAuthenticationViaFormAction" p:centralAuthenticationService-ref="centralAuthenticationService" p:warnCookieGenerator-ref="warnCookieGenerator"/>
5、要是程序能正常运行还需要一下配置
web.xml中添加
<servlet-mapping> <servlet-name>cas</servlet-name> <url-pattern>/captcha.htm</url-pattern> </servlet-mapping>
文件messages_zh_CN.properties添加,如下
login.code.tip=请输入验证码 login.code.error=验证码输入有误
三、效果
至此,验证码正常可以使用
相关推荐
springmvc+spring+shiro+cas单点登录实例 加入了登录验证码认证,修改了下首页样式,不过样式没有弄好,很丑的,有空自己再弄下 说明:cas-server是单点登录服务端,用的是maven项目,但是WEB-INF里面的lib目录下面...
CAS单点登录 验证码版本 只需要修改 数据地址就行了 如果需要源码可联系
**单点登录(Single Sign-On, SSO)**是一种身份验证机制,允许用户在一个系统上登录后,无需重新认证即可访问多个相互关联的系统。在本文中,我们将深入探讨基于CAS(Central Authentication Service)3.0实现的SSO...
CAS(Central Authentication Service)是一种广泛使用的单点登录(Single Sign-On, SSO)框架,它为Web应用程序提供了统一的身份验证服务。"cas-overlay-template-5.3" 是一个基于Spring Boot构建的CAS服务器覆盖...
在这个"登录 单点登录 带验证码登录"的实例中,我们将会深入探讨以下几个关键知识点: 1. **登录机制**:登录系统通常需要用户输入用户名和密码。在本示例中,登录过程可能是通过用户界面提交表单,后台Java代码...
总之,Jeecg配置单点登录是一个涉及CAS服务器设置、客户端集成、登录验证等多个环节的过程。正确配置后,能够提升用户体验,同时也强化了系统的安全性。通过学习和实践,你可以掌握这一重要技能,为自己的Java应用...
在IT行业中,单点登录(Single Sign-On, SSO)是一种高效的...参考提供的文档,如`单点登录部署步骤.doc`、`添加验证码.doc`,以及查看`applicationContext_login.xml`和Java代码,可以帮助理解并完成整个配置过程。
CAS(Central Authentication Service,中央认证服务)是一种广泛使用的开源单点登录(Single Sign-On, SSO)协议。SSO允许用户通过一次登录就能访问多个应用系统,而无需为每个系统分别进行身份验证。这种机制提高...
CAS(Central Authentication Service)是Java开发的一个开源的单点登录(Single Sign-On,简称SSO)框架,它为网络应用程序提供了一种集中化的身份验证服务。单点登录是指用户在一个应用系统中登录后,可以在其他...
4. **集成CAS Client**:在每个需要单点登录的应用系统中,添加CAS Client的依赖,配置相应设置。 5. **配置应用服务**:为每个应用系统配置服务定义,如在CAS Server中注册服务URL和服务ID。 6. **测试与调试**:...
### 使用CAS在Tomcat中实现单点登录的关键知识点 #### 一、CAS简介与特性 - **CAS**(Central Authentication Service)是由耶鲁大学发起的一个开源项目,它为Web应用程序提供了一种简单可靠且功能强大的单点登录...
CAS(Central Authentication Service)是 Yale 大学开发的一个开源项目,是实现单点登录的一种流行方案。本文档将深入探讨 CAS 单点登录的实现过程、核心技术和配置方法。 一、CAS 概述 CAS 是一个基于 Web 的...
CAS(Central Authentication Service)是一种开源的身份验证协议,常用于构建单点登录(Single Sign-On,SSO)系统。CAS服务器4.0是该协议的一个重要版本,提供了强大的安全功能和灵活的定制能力,尤其适合开发者在...
总的来说,CAS 4.2.7与Maven Overlay的结合使用,为SSO单点登录提供了灵活且强大的部署选项,使得企业可以方便地集成自己的数据库系统,实现用户的身份验证和管理。对于开发者而言,理解CAS的架构和配置流程,以及...
单点登录(Single Sign-On, 简称SSO)是一种网络访问控制机制,它允许用户在一次身份验证后访问多个应用系统,而无需再次输入凭证。CAS(Central Authentication Service)是 Yale 大学开发的一个开源项目,它提供了...
总结来说,"cas-client-3.2.1+cas-server-3.4.10"是实现网络应用SSO认证的一个解决方案,涉及到了客户端和服务端的配置、安全策略、用户认证流程、单点登出以及系统维护等多个方面。正确配置和使用这个组合,可以极...
CAS(Central Authentication Service)是一个开放源代码的单点登录(Single Sign-On,SSO)解决方案,主要用于网络应用程序的统一身份验证。"cas-server-4.0.0-release" 是CAS服务器的一个版本发布,包含了用于部署...
CAS(Central Authentication Service)是基于Java的开源身份验证框架,用于在网络上实现单点登录(Single Sign-On, SSO)。在本场景中,"cas_client.zip" 是一个包含CAS客户端组件的压缩包,它使得应用程序能够与...
1. 单点登录:用户只需一次登录,就能访问所有支持CAS的服务,减少了用户的记忆负担和登录步骤。 2. 服务验证:CAS服务器负责验证用户的身份,并向服务提供者提供认证票据,确保只有经过验证的用户才能访问服务。 3....
CAS(Central Authentication Service)是Java开发的一个开源身份验证框架,主要用于实现Web应用的单点登录(Single Sign-On,简称SSO)。它允许用户通过一个入口点登录,然后在整个系统内无需再次输入凭证即可访问...