结合springMVC,shiro,redis,kaptcha进行验证码登录完整随笔

自己在做项目的时候总结的配置全部流程,用作记录也希望能帮助大家。

一、进行kaptha的依赖配置

1  <!--验证码生成工具-->
2         <dependency>
3             <groupId>com.github.penggle</groupId>
4             <artifactId>kaptcha</artifactId>
5             <version>2.3.2</version>
6         </dependency>

二、web.xml配置,我们只需要简单配置一个 Servlet,页面通过 IMG 标签就可以展现图形验证码。

<servlet>
        <servlet-name>Kaptcha</servlet-name>
        <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
        <init-param>
        <param-name>kaptcha.image.width</param-name>
        <param-value>200</param-value>                
        </init-param>
        <init-param>
        <param-name>kaptcha.image.height</param-name>
        <param-value>50</param-value>                
        </init-param>
        <init-param>
        <param-name>kaptcha.textproducer.char.length</param-name>
        <param-value>4</param-value>                
        </init-param>
        <init-param>
        <param-name>kaptcha.noise.impl</param-name>
        <param-value>com.google.code.kaptcha.impl.NoNoise</param-value>                
        </init-param>
        <init-param>
        <param-name>kaptcha.session.key</param-name>
        <param-value>rand</param-value>                
        </init-param>
</servlet>
<servlet-mapping>
        <servlet-name>Kaptcha</servlet-name>
        <url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>

三、扩展 UsernamePasswordTokenShiro 表单认证,页面提交的用户名密码等信息,用 UsernamePasswordToken 类来接收,很容易想到,要接收页面验证码的输入,我们需要扩展此类:

package com.caa.shiro.filter;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

public class CaptchaUsernamePasswordToken extends UsernamePasswordToken {
    //验证码字符串
    private String captcha;
 
    public CaptchaUsernamePasswordToken(String username, String password,
            boolean rememberMe, String captcha) {
        super(username, password, rememberMe);
        this.captcha = captcha;
    }
 
    public String getCaptcha() {
        return captcha;
    }
 
    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }
    
}

四、

扩展 FormAuthenticationFilter

接下来我们扩展 FormAuthenticationFilter 类

这里贴一个全部的扩展方法,不过实际上我使用的是shiro自带的登录验证 后添加的验证码的校验方法(如果也是只需要添加验证码功能那只需要在你的控制层调用doCaptchaValidate方法即可

package com.caa.shiro.filter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class CaptchaFormAuthenticationFilter extends FormAuthenticationFilter {
    private static final Logger LOG = LoggerFactory.getLogger(CaptchaFormAuthenticationFilter.class);
    
    public CaptchaFormAuthenticationFilter() {
    }
    @Override
    /**
     * 登录验证
     */
    protected boolean executeLogin(ServletRequest request,
            ServletResponse response) throws Exception {
        CaptchaUsernamePasswordToken token = createToken(request, response);
        try {
            /*图形验证码验证*/
            doCaptchaValidate((HttpServletRequest) request, token);
            Subject subject = getSubject(request, response);
            subject.login(token);//正常验证
            LOG.info(token.getUsername()+"登录成功");
            return onLoginSuccess(token, subject, request, response);
        }catch (AuthenticationException e) {
            LOG.info(token.getUsername()+"登录失败--"+e);
            return onLoginFailure(token, e, request, response);
        }
    }
 
    // 验证码校验
    public void doCaptchaValidate(HttpServletRequest request,
            CaptchaUsernamePasswordToken token) {
//session中的图形码字符串
        HttpSession session = request.getSession();    
        String captcha = (String)session.getAttribute("rand");
//比对
        if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha())) {
            throw new IncorrectCaptchaException("验证码错误!");
        }
    }
 
    @Override
    protected CaptchaUsernamePasswordToken createToken(ServletRequest request,
            ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        String captcha = getCaptcha(request);
        boolean rememberMe = isRememberMe(request);
        String host = getHost(request);
 
        return new CaptchaUsernamePasswordToken(username,
                password, rememberMe, captcha);
    }
 
    public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
 
    private String captchaParam = DEFAULT_CAPTCHA_PARAM;
 
    public String getCaptchaParam() {
        return captchaParam;
    }
 
    public void setCaptchaParam(String captchaParam) {
        this.captchaParam = captchaParam;
    }
 
    protected String getCaptcha(ServletRequest request) {
        return WebUtils.getCleanParam(request, getCaptchaParam());
    }
    
//保存异常对象到request
    @Override
    protected void setFailureAttribute(ServletRequest request,
            AuthenticationException ae) {
        request.setAttribute(getFailureKeyAttribute(), ae);
    }
}

六、前面验证码校验不通过,我们抛出一个异常 IncorrectCaptchaException,此类继承 AuthenticationException,之所以需要扩展一个新的异常类,为的是在页面能更精准显示错误提示信息。

package com.caa.shiro.filter;

 
import org.apache.shiro.authc.AuthenticationException;
 
public class IncorrectCaptchaException extends AuthenticationException {
 
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public IncorrectCaptchaException() {
        super();
    }
 
    public IncorrectCaptchaException(String message, Throwable cause) {
        super(message, cause);
    }
 
    public IncorrectCaptchaException(String message) {
        super(message);
    }
 
    public IncorrectCaptchaException(Throwable cause) {
        super(cause);
    }
}

七、shiro配置(Filter的配置及使用)这段因为我的配置文件东西较多 在网上贴了一段 核心东西都有

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
 
    <!-- Shiro Filter 拦截器相关配置 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- securityManager -->
        <property name="securityManager" ref="securityManager" />
        <!-- 登录路径 -->
        <property name="loginUrl" value="/login.jsp" />
        <!-- 登录成功后跳转路径 -->
        <property name="successUrl" value="/pages/index.jsp" />
        <!-- 授权失败跳转路径 -->
        <property name="unauthorizedUrl" value="/login.jsp" />
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="myAuthenFilter" />
            </util:map>
        </property>
        <!-- 过滤链定义 -->
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = authc
                /pages/* = authc
                /index.jsp* = authc
                /logout.do = logout
                <!-- 访问这些路径必须拥有某种权限 /role/edit/* = perms[role:edit] /role/save = perms[role:edit] 
                    /role/list = perms[role:view] -->
            </value>
        </property>
    </bean>
 
    <!-- 自定义验证拦截器 -->
    <bean id="myAuthenFilter" class="javacommon.shiro.CaptchaFormAuthenticationFilter" />
 
    <!-- securityManager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm" />
    </bean>
 
    <!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
        <property name="cacheManager" ref="cacheManager" /> </bean> -->
 
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
 
    <!-- 自定义Realm实现 -->
    <bean id="myRealm" class="javacommon.shiro.CustomRealm">
        <!-- <property name="cacheManager" ref="shiroCacheManager" /> -->
    </bean>
</beans>

八、前台页面(本人用的是html加vue的,不过前天东西不多可以根据自己的页面来修改)

<body class="hold-transition login-page" style="background: black url(statics/images/login-bg.jpg) no-repeat fixed top;">
    <div class="login-box" id="rrapp" style="margin-top: 12%" v-cloak>
      <div class="login-box-body">
          <p class="login-box-msg" style="font-size: 25px;font-weight:bold">中拍协后台管理系统</p>
          <div v-if="error" class="alert alert-danger alert-dismissible">
            <h4 style="margin-bottom: 0px;"><i class="fa fa-exclamation-triangle"></i> {{errorMsg}}</h4>
          </div>
          <div class="form-group has-feedback">
            <input type="text" class="form-control" v-model="username" @keyup.enter="login" placeholder="账号" autofocus>
            <span class="fa fa-user form-control-feedback"></span>
          </div>
          <div class="form-group has-feedback">
            <input type="password" class="form-control" v-model="password" @keyup.enter="login" placeholder="密码">
            <span class="fa fa-lock form-control-feedback"></span>
          </div>
          <div >
            <input type="text" id="code" name="captcha" v-model="captcha" @keyup.enter="login" class="form-control" placeholder="验证码">
        </div>
        <div >
            <img id="codeImg" src="kaptcha.jpg" class="img-responsive" style="display:inline" @click="refreshCaptcha">(看不清<a href="javascript:void(0)" @click="refreshCaptcha" style="display:inline">换一张</a>)
        </div>
          <div class="checkbox">
              <label>
                <input type="checkbox" name="isRememberMe" v-model="isRememberMe">记住我,下次免登陆
              </label>
          </div>
          <div class="row">
            <div class="col-xs-12">
              <button type="button" class="btn btn-block btn-success btn-lg" @click="login">登录</button>
            </div>
          </div>
      </div>
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el:'#rrapp',
            data:{
                username: '',
                password: '',
                captcha: '',
                error: false,
                errorMsg: '',
                isRememberMe:false
            },
            beforeCreate: function(){
                if(self != top){
                    top.location.href = self.location.href;
                }
            },
            methods: {
                login: function (event) {
                    var data = "username="+vm.username+"&password="+vm.password+"&isRememberMe="+vm.isRememberMe+"&captcha="+vm.captcha;
                    $.ajax({
                        type: "POST",
                        url: "login",
                        data: data,
                        dataType: "json",
                        success: function(result){
                            if(result.code == 0){//登录成功
                                parent.location.href ='index.html';
                            }else{
                                vm.error = true;
                                vm.errorMsg = result.msg;
                                document.getElementById("codeImg").src="kaptcha.jpg?t=" + Math.random();
                            }
                        }
                    });
                },
                refreshCaptcha: function () {
                    document.getElementById("codeImg").src="kaptcha.jpg?t=" + Math.random();
                }
            }
        });
    </script>
</body>

 

posted @ 2018-07-05 17:53  夜的囚徒  阅读(1696)  评论(0编辑  收藏  举报