当前位置: 代码网 > it编程>编程语言>Java > SpringBoot整合SpringSecurity实现图形验证码功能

SpringBoot整合SpringSecurity实现图形验证码功能

2024年12月30日 Java 我要评论
1、图形验证码的作用图形验证码(captcha,completely automated public turing test to tell computers and humans apart)是

1、图形验证码的作用

图形验证码(captcha,completely automated public turing test to tell computers and humans apart)是一种用于区分用户是人类还是计算机程序的自动化测试。它通常用于防止自动化软件(如机器人或爬虫程序)进行恶意操作,如滥用在线服务、暴力破 解密码或进行垃圾邮件发送等。

图形验证码的工作原理基于一个假设:计算机程序难以自动识别和处理复杂的图像或模式,而人类则相对容易。因此,图形验证码通常包含扭曲的文字、数字、图像或它们的组合,这些元素对人类来说相对容易辨认,但对计算机程序来说则非常困难。

下面将介绍 spring boot 整合 spring security 实现图形验证码功能,执行结果如下如:

(1)登录页面

(2)登录成功后,跳转至首页

2、创建项目

【示例】springboot 整合 springsecurity  使用过滤器实现图形验证码功能。

2.1 创建 spring boot 项目

创建 springboot 项目,项目结构如下图:

2.2 添加 maven 依赖

在 pom.xml 配置文件中添加 spring security、谷歌 kaptcha 图形验证码。

<!-- spring security 依赖 -->
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-security</artifactid>
    <version>2.7.18</version>
</dependency>
 
<!-- 谷歌 kaptcha 图形验证码 -->
<dependency>
    <groupid>com.github.penggle</groupid>
    <artifactid>kaptcha</artifactid>
    <version>2.3.2</version>
</dependency>

3、整合 spring security 框架实现认证与授权

3.1 配置类(config 层)

创建 websecurityconfig 类(spring security 配置类),并添加 @enablewebsecurity 注解和继承 websecurityconfigureradapter 类。

package com.pjb.securitydemo.config;
 
import com.pjb.securitydemo.filter.verificationcodefilter;
import com.pjb.securitydemo.handler.loginfailurehandler;
import com.pjb.securitydemo.handler.loginsuccesshandler;
import com.pjb.securitydemo.handler.permissiondeniedhandler;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.context.annotation.bean;
import org.springframework.security.config.annotation.web.builders.httpsecurity;
import org.springframework.security.config.annotation.web.configuration.enablewebsecurity;
import org.springframework.security.config.annotation.web.configuration.websecurityconfigureradapter;
import org.springframework.security.core.userdetails.user;
import org.springframework.security.core.userdetails.userdetailsservice;
import org.springframework.security.crypto.password.nooppasswordencoder;
import org.springframework.security.crypto.password.passwordencoder;
import org.springframework.security.provisioning.inmemoryuserdetailsmanager;
import org.springframework.security.web.authentication.usernamepasswordauthenticationfilter;
 
/**
 * spring security 配置类
 * @author pan_junbiao
 **/
@enablewebsecurity
public class websecurityconfig extends websecurityconfigureradapter
{
    @autowired
    private loginsuccesshandler loginsuccesshandler;
 
    @autowired
    private loginfailurehandler loginfailurehandler;
 
    @autowired
    private permissiondeniedhandler permissiondeniedhandler;
 
    @override
    protected void configure(httpsecurity http) throws exception
    {
        http.authorizerequests() //返回一个url拦截注册器
                .antmatchers("/captcha.jpg").permitall() //公开其权限
                .anyrequest() //匹配所有的请求
                .authenticated() //所有匹配的url都需要被认证才能访问
                .and() //结束当前标签,让上下文回到 httpsecurity
                .formlogin() //启动表单认证
                .loginpage("/mylogin.html") //自定义登录页面
                .loginprocessingurl("/auth/form") //指定处理登录请求路径
                .permitall() //使登录页面不设限访问
                //.defaultsuccessurl("/index") //登录认证成功后的跳转页面
                .successhandler(loginsuccesshandler) //指定登录成功时的处理
                .failurehandler(loginfailurehandler) //指定登录失败时的处理
                .and()
                .exceptionhandling().accessdeniedhandler(permissiondeniedhandler) //403无权时的返回操作
                .and().csrf().disable(); //关闭csrf的防御功能
        
        //图形验证码过滤器(核心代码):将自定义过滤器添加在usernamepasswordauthenticationfilter之前
        http.addfilterbefore(new verificationcodefilter(), usernamepasswordauthenticationfilter.class);
    }
 
    /**
     * 内存中添加登录账号
     */
    @bean
    public userdetailsservice userdetailsservice()
    {
        inmemoryuserdetailsmanager manager = new inmemoryuserdetailsmanager();
        manager.createuser(user.withusername("admin").password("123456").roles("admin").build());
        manager.createuser(user.withusername("user").password("123456").roles("user").build());
        manager.createuser(user.withusername("panjunbiao").password("123456").roles("user").build());
        return manager;
    }
 
    /**
     * 密码编译器
     * 由于5.x版本之后默认启用了委派密码编译器,
     * 因而按照以往的方式设置内存密码将会读取异常,
     * 所以需要暂时将密码编码器设置为 nooppasswordencoder
     */
    @bean
    public passwordencoder passwordencoder()
    {
        return nooppasswordencoder.getinstance();
    }
}

3.2 处理类(handler 层)

(1)登录成功处理类

package com.pjb.securitydemo.handler;
 
import org.springframework.security.core.authentication;
import org.springframework.security.web.authentication.authenticationsuccesshandler;
import org.springframework.stereotype.component;
 
import javax.servlet.servletexception;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
 
/**
 * 登录成功处理类
 */
@component
public class loginsuccesshandler implements authenticationsuccesshandler
{
    @override
    public void onauthenticationsuccess(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, authentication authentication) throws ioexception, servletexception
    {
        //重定向至首页
        httpservletresponse.sendredirect("/");
    }
}

(2)登录失败处理类

package com.pjb.securitydemo.handler;
 
import com.pjb.securitydemo.exception.verificationcodeexception;
import org.springframework.security.authentication.badcredentialsexception;
import org.springframework.security.authentication.credentialsexpiredexception;
import org.springframework.security.authentication.disabledexception;
import org.springframework.security.authentication.lockedexception;
import org.springframework.security.core.authenticationexception;
import org.springframework.security.core.userdetails.usernamenotfoundexception;
import org.springframework.security.web.authentication.authenticationfailurehandler;
import org.springframework.stereotype.component;
 
import javax.servlet.servletexception;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.io.printwriter;
 
/**
 * 登录失败处理类
 */
@component
public class loginfailurehandler implements authenticationfailurehandler
{
    @override
    public void onauthenticationfailure(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, authenticationexception authenticationexception) throws ioexception, servletexception
    {
        //获取登录失败原因
        string errormessage = "";
        if(authenticationexception instanceof badcredentialsexception){
            errormessage = "用户名或密码不正确";
        }else if(authenticationexception instanceof disabledexception){
            errormessage = "账号被禁用";
        }else if(authenticationexception instanceof usernamenotfoundexception){
            errormessage = "用户名不存在";
        }else if(authenticationexception instanceof credentialsexpiredexception){
            errormessage = "密码已过期";
        }else if(authenticationexception instanceof lockedexception) {
            errormessage = "账号被锁定";
        }else if(authenticationexception instanceof verificationcodeexception){
            errormessage = "无效的图形验证码";
        }else{
            errormessage = "未知异常";
        }
 
        //设置响应编码
        httpservletresponse.setcontenttype("application/json;charset=utf-8");
        printwriter out = httpservletresponse.getwriter();
        out.write(errormessage);
    }
}

(3)403无权限处理类

package com.pjb.securitydemo.handler;
 
import org.springframework.security.access.accessdeniedexception;
import org.springframework.security.web.access.accessdeniedhandler;
import org.springframework.stereotype.component;
 
import javax.servlet.servletexception;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.io.printwriter;
 
/**
 * 403无权限处理类
 */
@component
public class permissiondeniedhandler implements accessdeniedhandler
{
    @override
    public void handle(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, accessdeniedexception e) throws ioexception, servletexception
    {
        httpservletresponse.setcontenttype("application/json;charset=utf-8");
        printwriter out = httpservletresponse.getwriter();
        out.write("403无权限");
    }
}

4、整合 kaptcha 框架实现图形验证码

4.1 配置类(config 层)

创建 kaptchaconfig 类(kaptcha 图形验证码配置类),设置图形验证码相关属性。

package com.pjb.securitydemo.config;
 
import com.google.code.kaptcha.producer;
import com.google.code.kaptcha.impl.defaultkaptcha;
import com.google.code.kaptcha.util.config;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
 
import java.util.properties;
 
/**
 * 谷歌kaptcha图形验证码配置类
 */
@configuration
public class kaptchaconfig
{
    @bean
    public producer captcha()
    {
        //配置图形验证码的基本参数
        properties properties = new properties();
        //图片宽度
        properties.setproperty("kaptcha.image.width","150");
        //图片长度
        properties.setproperty("kaptcha.image.height","50");
        //字符集(从哪些字符中产生)
        properties.setproperty("kaptcha.textproducer.char.string", "0123456789");
        //字符长度
        properties.setproperty("kaptcha.textproducer.char.length", "4");
        //字体颜色
        properties.put("kaptcha.textproducer.font.color", "red");
        // 文字间隔,这里设置为10px
        properties.put("kaptcha.textproducer.char.space", "10");
 
        // 背景颜色渐变开始
        properties.put("kaptcha.background.clear.from", "yellow");
        // 背景颜色渐变结束
        properties.put("kaptcha.background.clear.to", "green");
 
        //初始化配置
        config config = new config(properties);
 
        //使用默认的图形验证码实现,当然也可以自定义实现
        defaultkaptcha defaultkaptcha = new defaultkaptcha();
        defaultkaptcha.setconfig(config);
        return defaultkaptcha;
    }
}

图形验证码配置属性表

属性名属性作用默认值
kaptcha.border图片边框,合法值:yes , noyes
kaptcha.border.color边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue.black
kaptcha.image.width图片宽200
kaptcha.image.height图片高50
kaptcha.producer.impl图片实现类com.google.code.kaptcha.impl.defaultkaptcha
kaptcha.textproducer.impl文本实现类com.google.code.kaptcha.text.impl.defaulttextcreator
kaptcha.textproducer.char.string文本集合,验证码值从此集合中获取abcde2345678gfynmnpwx
kaptcha.textproducer.char.length验证码长度5
kaptcha.textproducer.font.names字体arial, courier
kaptcha.textproducer.font.size字体大小40px.
kaptcha.textproducer.font.color字体颜色,合法值: r,g,b 或者 white,black,blue.black
kaptcha.textproducer.char.space文字间隔2
kaptcha.noise.impl干扰实现类com.google.code.kaptcha.impl.defaultnoise
kaptcha.noise.color干扰 颜色,合法值: r,g,b 或者 white,black,blue.black
kaptcha.obscurificator.impl

图片样式:<br />水纹 com.google.code.kaptcha.impl.waterripple <br />

鱼眼 com.google.code.kaptcha.impl.fisheyegimpy <br />

阴影 com.google.code.kaptcha.impl.shadowgimpy

com.google.code.kaptcha.impl.waterripple
kaptcha.background.impl背景实现类com.google.code.kaptcha.impl.defaultbackground
kaptcha.background.clear.from背景颜色渐变,开始颜色light grey
kaptcha.background.clear.to背景颜色渐变, 结束颜色white
kaptcha.word.impl文字渲染器com.google.code.kaptcha.text.impl.defaultwordrenderer
kaptcha.session.keysession keykaptcha_session_key
kaptcha.session.datesession date

4.2 控制器层(controller 层)

创建 captchacontroller 类(验证码控制器),实现生成验证码图片方法。

package com.pjb.securitydemo.controller;
 
import com.google.code.kaptcha.producer;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.getmapping;
 
import javax.imageio.imageio;
import javax.servlet.servletoutputstream;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.awt.image.bufferedimage;
import java.io.ioexception;
 
/**
 * 验证码控制器
 */
@controller
public class captchacontroller
{
    @autowired
    private producer captchaproducer;
 
    @getmapping("/captcha.jpg")
    public void getcaptcha(httpservletrequest request, httpservletresponse response) throws ioexception
    {
        //设置内容类型
        response.setcontenttype("image/jpeg");
        //创建验证码文本
        string captext = captchaproducer.createtext();
 
        //将验证码文本保存到session中
        request.getsession().setattribute("captcha", captext);
        //创建验证码图片
        bufferedimage bufferedimage = captchaproducer.createimage(captext);
        //获取响应输出流
        servletoutputstream out = response.getoutputstream();
        //将图片验证码数据写入响应输出流
        imageio.write(bufferedimage,"jpg",out);
        //推送并关闭响应输出流
        try
        {
            out.flush();
        }
        finally
        {
            out.close();
        }
    }
}

4.3 自定义异常类(exception 层)

自定义异常类 verificationcodeexception(验证码校验失败的异常类),继承 authenticationexception 类。

package com.pjb.securitydemo.exception;
 
import org.springframework.security.core.authenticationexception;
 
/**
 * 验证码校验失败的异常类
 */
public class verificationcodeexception extends authenticationexception
{
    public verificationcodeexception()
    {
        super("图形验证码校验失败");
    }
}

4.4 自定义过滤器(filter 层)

自定义过滤器类 verificationcodefilter (验证码校验过滤器),继承 onceperrequestfilter 类。

有了图形验证码的 api 之后,就可以自定义验证码校验过滤器了。虽然 spring security 的过滤器链对过滤器没有特殊要求,只要继承 filter 接口即可,但是在 spring 体系中,推荐使用  onceperrequestfilter 类来实现,它可以确保一次请求只会通过一次该过滤器(filter 实际上并不能保证这一点)。

package com.pjb.securitydemo.filter;
 
import com.pjb.securitydemo.exception.verificationcodeexception;
import com.pjb.securitydemo.handler.loginfailurehandler;
import org.springframework.security.web.authentication.authenticationfailurehandler;
import org.springframework.util.stringutils;
import org.springframework.web.filter.onceperrequestfilter;
 
import javax.servlet.filterchain;
import javax.servlet.servletexception;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import javax.servlet.http.httpsession;
import java.io.ioexception;
 
/**
 * 验证码校验过滤器
 */
public class verificationcodefilter extends onceperrequestfilter
{
    private authenticationfailurehandler authenticationfailurehandler = new loginfailurehandler();
 
    @override
    protected void dofilterinternal(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, filterchain filterchain) throws servletexception, ioexception
    {
        //非登录请求不校验验证码
        string requesturi = httpservletrequest.getrequesturi();
        if(!"/auth/form".equals(requesturi))
        {
            filterchain.dofilter(httpservletrequest,httpservletresponse);
        }
        else
        {
            try
            {
                //验证码校验
                verificationcode(httpservletrequest);
 
                //验证成功
                filterchain.dofilter(httpservletrequest,httpservletresponse);
            }
            catch (verificationcodeexception ex)
            {
                //验证失败
                authenticationfailurehandler.onauthenticationfailure(httpservletrequest, httpservletresponse, ex);
            }
        }
    }
 
    /**
     * 验证码校验
     */
    public void verificationcode(httpservletrequest httpservletrequest) throws verificationcodeexception
    {
        string requestcode = httpservletrequest.getparameter("captcha");
        httpsession session = httpservletrequest.getsession();
        string savedcode = (string)session.getattribute("captcha");
        if(!stringutils.isempty(savedcode))
        {
            //随手清除验证码,无论是失败,还是成功。客户端应在登录失败时刷新验证码
            session.removeattribute("captcha");
        }
 
        //验证不通过,抛出异常
        if(stringutils.isempty(requestcode) || stringutils.isempty(savedcode) || !requestcode.equals(savedcode))
        {
            throw new verificationcodeexception();
        }
    }
}

至此整合 kaptcha 框架实现图形验证码已完成,最后注意,一定要把自定义过滤器类 verificationcodefilter 添加到 spring security 的过滤器链中。

打开 websecurityconfig 类(spring security 配置类),将自定义过滤器类 verificationcodefilter 添加到过滤器链中,如下:

5、前端页面

5.1 控制器层(controller 层)

创建 indexcontroller 类(首页控制器),实现获取当前登录用户名并跳转至首页。

package com.pjb.securitydemo.controller;
 
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.requestmapping;
 
import javax.servlet.http.httpservletrequest;
import java.security.principal;
 
/**
 * 首页控制器
 * @author pan_junbiao
 **/
@controller
public class indexcontroller
{
    /**
     * 首页
     */
    @requestmapping("/")
    public string index(httpservletrequest request)
    {
        //获取当前登录人
        string username = "未登录";
        principal principal = request.getuserprincipal();
        if(principal!=null)
        {
            username = principal.getname();
        }
 
        //返回页面
        request.setattribute("username",username);
        return "/index.html";
    }
 
}

5.2 编写登录页面

在 resources\static 静态资源目录下,创建 mylogin.html 页面。

注意:mylogin.html 页面必须放在 resources\static 静态资源目录下,否则页面无法加载。

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>登录</title>
    <meta name="author" content="pan_junbiao的博客">
</head>
<body>
<form name="myform" action="/auth/form" method="post">
    <table align="center">
        <caption>用户登录</caption>
        <tr>
            <td>登录账户:</td>
            <td>
                <input type="text" name="username" placeholder="请输入登录账户" value="panjunbiao" />
            </td>
        </tr>
        <tr>
            <td>登录密码:</td>
            <td>
                <input type="password" name="password" placeholder="请输入登录密码" value="123456" />
            </td>
        </tr>
        <tr>
            <td>验证码:</td>
            <td>
                <!-- 新增图形验证码的输入框 -->
                <input type="text" name="captcha" placeholder="请输入验证码" />
 
                <!-- 图片指向图形验证码api -->
                <img src="/captcha.jpg" alt="captch" height="50px" width="150px" style="margin-left:20px;" >
            </td>
        </tr>
        <!-- 以下是提交、取消按钮 -->
        <tr>
            <td colspan="2" style="text-align: center; padding: 5px;">
                <input type="submit" value="提交" />
                <input type="reset" value="重置" />
            </td>
        </tr>
    </table>
</form>
</body>
</html>

5.3 编写首页

在 resources\templates 资源目录下,创建 index.html 页面。

注意:首页 index.html 页面中使用 thymeleaf 模板 。

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>首页</title>
    <meta name="author" content="pan_junbiao的博客">
</head>
<body>
    <h1 style="color: red">hello,spring security</h1>
    <p>博客信息:您好,欢迎访问 pan_junbiao的博客</p>
    <p>博客地址:https://blog.csdn.net/pan_junbiao</p>
    <p th:text="'当前登录人:' + ${username}"></p>
    <a href="/logout" rel="external nofollow"  onclick="return confirm('确认注销吗?');">登出</a>
</body>
</html>

6、运行项目

6.1 登录页面

6.2 图形验证码校验失败

6.3 登录成功后,跳转至首页

以上就是springboot整合springsecurity实现图形验证码功能的详细内容,更多关于springboot springsecurity图形验证码的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com