一般来说,登录时都会要求用户输入验证码,以防止恶意登录。
可是,SpringSecurity并没有为我们提供这样的功能,所以就需要我们自己来解决了。
那么该如何解决呢,其实也挺简单的,
核对验证码信息,当然是在登录时处理的,所以,使用filter是最佳选择。
既然确定了方向,那么久该考虑如何增加这个filter了,其实可以有两种方案:
1.继承UsernamePasswordAuthenticationFilter ,在里面增加验证码验证规则即可;
2.自己定义一个filter,专门处理验证码验证;
笔者这里推荐使用第二种方案,因为第一种我们需要显示声明login-filter,所以需要修改SpringSecurity的配置文件,而方案 2根本不需要修改,只需要向一般的filter一样,在web.xml中增加配置即可,唯一需要注意的是,mapping一定要与触发login- filter的url一致,如下所示:
- < filter >
- < filter-name > imageFilter </ filter-name >
- < filter-class > com.piaoyi.common.filter.ImageFilter </ filter-class >
- </ filter >
- < filter-mapping >
- < filter-name > imageFilter </ filter-name >
- < url-pattern > /j_spring_security_check </ url-pattern >
- </ filter-mapping >
另外需要注意的是,该filter的mapping一定要声明在springSecurityFilterChain的mapping之前,这样可以保证请求先被imageFilter处理。
设计思路就是这样的,这里给出一个简单的实现。
login.jsp
- < %@ page language = "java" contentType = "text/html; charset=UTF-8"
- pageEncoding = "UTF-8" % >
- < %@ include file = "/WEB-INF/views/jsp/common/includes.jsp" % >
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- < html >
- < head >
- < meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" >
- < title > 登陆 </ title >
- </ head >
- < body >
- < center >
- < div class = "error" >
- ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message }</ div >
- < form name = 'f' id = 'f'
- action = '<%=request.getContextPath()%>/j_spring_security_check'
- method = 'POST' >
- < table style = "width: 50%" >
- < tr >
- < td style = "text-align: right; width: 35%" > 用户名称 : </ td >
- < td style = "text-align: left" > < input type = 'text'
- name = 'j_username' value = '' > </ td >
- </ tr >
- < tr >
- < td style = "text-align: right" > 密码 : </ td >
- < td style = "text-align: left" > < input type = 'password'
- name = 'j_password' /> </ td >
- </ tr >
- < tr >
- < td style = "text-align: right" > < label for = "j_captcha" > 验证码: </ label > </ td >
- < td >
- < input type = 'text' name = 'j_captcha' class = "required"
- size = '5' />
- < img id = "imageF" src = "<c:url value=" /resource/image.jsp" /> " />
- < a href = "#" id = "flashImage" > 看不清楚换一张 </ a >
- </ td >
- </ tr >
- < tr >
- < td align = "right" > < input id = "_spring_security_remember_me"
- name = "_spring_security_remember_me" type = "checkbox" value = "true" />
- </ td >
- < td > < label for = "_spring_security_remember_me" > Remember Me? </ label >
- </ td >
- </ tr >
- < tr >
- < td colspan = "2" style = "text-align: center" > < input
- type = "submit" name = "submit" value = "登录" /> </ td >
- </ tr >
- </ table >
- </ form >
- </ center >
- < script type = "text/javascript" >
- document.f.j_username.focus();
- if ('${message}' == 1) {
- alert("用户名或密码错误");
- }
- jQuery(function($){
- $("#flashImage").click(function(){
- $('#imageF').hide().attr('src','< c:url value = "/resource/image.jsp" /> '+ '?'+ Math.floor(Math.random() * 100)).fadeIn();
- });
- });
- </ script >
- </ body >
- </ html >
ImageFilter.java
- package com.piaoyi.common.filter;
- import java.io.IOException;
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class ImageFilter implements Filter{
- @Override
- public void destroy() {
- // TODO Auto-generated method stub
- }
- @Override
- public void doFilter(ServletRequest arg0, ServletResponse arg1,
- FilterChain arg2) throws IOException, ServletException {
- // TODO Auto-generated method stub
- HttpServletRequest request = (HttpServletRequest) arg0;
- HttpServletResponse response = (HttpServletResponse) arg1;
- String yanzhengm = request.getParameter("j_captcha" );
- String sessionyanz = (String)request.getSession(true ).getAttribute( "yzkeyword" );
- if (yanzhengm.equals(sessionyanz)){
- arg2.doFilter(request, response);
- }else {
- response.sendRedirect("/login.do" );
- }
- }
- @Override
- public void init(FilterConfig arg0) throws ServletException {
- // TODO Auto-generated method stub
- }
- }
image.jsp
- < %@ page contentType = "image/jpeg" import = "java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" pageEncoding = "UTF-8" % >
- < %!
- Color getRandColor(int fc,int bc){//随机获得颜色,RGB格式
- 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);
- }
- %>
- < %
- //清除缓存,每次访问该页面时都从服务器端读取
- response.setHeader("Pragma","No-cache");
- response.setHeader("Cache-Control","no-cache");
- response.setDateHeader("Expires", 0);
- // 定义显示图片的宽度和高度
- 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));
- // 设置线条颜色并画线,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);
- }
- // 保存进session
- session.setAttribute("yzkeyword",sRand);
- //System.out.println("yzm:"+sRand);
- // 显示图片
- g.dispose();
- %>
- < %
- //转换成一张图片,格式为JPEG
- ImageIO.write(image, "JPEG", response.getOutputStream());
- out.clear();//清空缓存的内容。
- pageContext.pushBody();
- %>
applicationContext-security.xml
- <? xml version = "1.0" encoding = "UTF-8" ?>
- < beans:beans xmlns = "http://www.springframework.org/schema/security"
- xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:p = "http://www.springframework.org/schema/p"
- xmlns:aop = "http://www.springframework.org/schema/aop" xmlns:context = "http://www.springframework.org/schema/context"
- xmlns:jee = "http://www.springframework.org/schema/jee" xmlns:tx = "http://www.springframework.org/schema/tx"
- xmlns:util = "http://www.springframework.org/schema/util" xmlns:mvc = "http://www.springframework.org/schema/mvc"
- xmlns:tool = "http://www.springframework.org/schema/tool" xmlns:beans = "http://www.springframework.org/schema/beans"
- xsi:schemaLocation ="
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
- http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
- http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.1.xsd
- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"
- default-lazy-init = "true" >
- < http security = "none" pattern = "/index.do" />
- < http auto-config = 'true' access-decision-manager-ref = "accessDecisionManager"
- access-denied-page = "/index.do" >
- < intercept-url pattern = "/demo.do*" access = "IS_AUTHENTICATED_REMEMBERED" />
- < intercept-url pattern = "/**/*.do*" access = "HODLE" />
- < logout logout-url = "/logout.do" invalidate-session = "true"
- logout-success-url = "/logout.jsp" />
- < form-login login-page = "/index.do" default-target-url = "/frame.do"
- always-use-default-target = "true" authentication-failure-url = "/index.do?login_error=1" />
- < session-management >
- < concurrency-control max-sessions = "1" expired-url = "/timeout.jsp" />
- </ session-management >
- < remember-me key = "jbcpPetStore" />
- </ http >
- <!-- Automatically receives AuthenticationEvent messages -->
- < beans:bean id = "loggerListener"
- class = "org.springframework.security.authentication.event.LoggerListener" />
- < authentication-manager erase-credentials = "false" >
- < authentication-provider user-service-ref = "userService" >
- < password-encoder hash = "md5" />
- </ authentication-provider >
- </ authentication-manager >
- < beans:bean id = "userService" class = "com.piaoyi.common.security.UserService" />
- < beans:bean id = "accessDecisionManager"
- class = "org.springframework.security.access.vote.AffirmativeBased" >
- < beans:property name = "decisionVoters" >
- < beans:list >
- < beans:bean class = "org.springframework.security.access.vote.RoleVoter" />
- < beans:bean
- class = "org.springframework.security.access.vote.AuthenticatedVoter" />
- < beans:bean class = "com.piaoyi.common.security.DynamicRoleVoter" />
- </ beans:list >
- </ beans:property >
- </ beans:bean >
- </ beans:beans >
效果图如下:
另外,从网上也看到了类似的解决方案: