欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Shiro集成Google Kaptcha验证码

程序员文章站 2022-06-28 16:36:57
Shiro集成Google Kaptcha验证码Kaptcha 简介Kaptcha 是一个可高度配置的实用验证码生成工具。Kaptcha 详细配置Constant描述默认值kaptcha.border图片边框,合法值:yes , noyeskaptcha.border.color边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue.blackkaptcha.border.thickness边框厚度,合法值:>01kaptcha.ima...

Shiro集成Google Kaptcha验证码

Kaptcha 简介

Kaptcha 是一个可高度配置的实用验证码生成工具。

Kaptcha 详细配置

Constant 描述 默认值
kaptcha.border 图片边框,合法值:yes , no yes
kaptcha.border.color 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black
kaptcha.border.thickness 边框厚度,合法值:>0 1
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 图片样式: 
水纹com.google.code.kaptcha.impl.WaterRipple 
鱼眼com.google.code.kaptcha.impl.FishEyeGimpy
阴影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.key session key KAPTCHA_SESSION_KEY
kaptcha.session.date session date KAPTCHA_SESSION_DATE

Maven 依赖

<!-- Google Kaptcha 验证码-->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

配置

@Component
public class KaptchaConfig {
    // 页面提交的验证码参数
    public static final String LOGIN_KAPTCHA = "captcha";
    // session中的验证码
    public static final  String SHIRO_VERIFY_SESSION = "verify_session_code";

    @Bean
    public DefaultKaptcha defaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 边框
        properties.setProperty("kaptcha.border", "yes");
        // 边框颜色
        properties.setProperty("kaptcha.border.color", "105,179,90");
        // 字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,黑体");
        // 字体尺寸
        properties.setProperty("kaptcha.textproducer.font.size", "26");
        // 字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        // 去掉干扰线
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        // 图片宽度
        properties.setProperty("kaptcha.image.width", "120");
        // 图片高度
        properties.setProperty("kaptcha.image.height", "30");
        // 图片样式
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
        // 设置session key
        properties.setProperty("kaptcha.session.key", "verification_session_key");
        // 验证码长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

Shiro拦截器

public class ShiroKaptchaFilter extends AccessControlFilter {
    private static final String MESSAGE = "message";
    // 错误后的跳转地址
    private String loginUrl;

    ShiroKaptchaFilter(String loginUrl){
        this.loginUrl = loginUrl;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        // 清除此提示,防止重复提示
        httpServletRequest.removeAttribute(MESSAGE);

        // 因为登录为表单提交登录,此处判断是否为表单提交
        if ("post".equalsIgnoreCase(httpServletRequest.getMethod())) {
            Subject subject = SecurityUtils.getSubject();
            String captcha__session = StringHelper.parse(subject.getSession().getAttribute(KaptchaConfig.SHIRO_VERIFY_SESSION), "");

            // 判断session中的验证码是否为空,为空则说明可能为第一次进入登录页面
            if (!StringHelper.isNullOrEmpty(captcha__session, true)) {
                String captcha__param = httpServletRequest.getParameter(KaptchaConfig.LOGIN_KAPTCHA);
                return captcha__session.equalsIgnoreCase(captcha__param);
            }
        }
        return true;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        // 重定向到登录页,并给出提示
        Map<String, String> queryParamMap = new HashMap<>();
        queryParamMap.put(MESSAGE, "验证码错误");
        WebUtils.issueRedirect(servletRequest, servletResponse, loginUrl, queryParamMap);
        return false;
    }
}

Shiro配置

@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    ShiroFilterFactory shiroFilterFactoryBean = new ShiroFilterFactory();
    // 必须设置 SecurityManager
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    
    // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
    shiroFilterFactoryBean.setLoginUrl("/admin/login");
    // 未授权界面;
    shiroFilterFactoryBean.setUnauthorizedUrl("/admin/login");
    // 登录成功后要跳转的链接
    shiroFilterFactoryBean.setSuccessUrl("/admin");

    Map<String, Filter> filterMap = new LinkedHashMap<>();
    // 验证码过滤器
    ShiroKaptchaFilter shiroKaptchaFilter = new ShiroKaptchaFilter("/admin/login");
    filterMap.put("kaptcha", shiroKaptchaFilter);

    //退出过滤器
    SystemLogoutFilter systemLogoutFilter = new SystemLogoutFilter();
    filterMap.put("logout", systemLogoutFilter);

    shiroFilterFactoryBean.setFilters(filterMap);

    //LinkedHashMap保证过滤顺序,前缀相同时先配置不会被拦截的链接,不能颠倒。
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    filterChainDefinitionMap.put("/admin/login/**", "kaptcha,anon");//验证码过滤器必须与登录确认在一起
    filterChainDefinitionMap.put("/admin/logout", "logout");
    filterChainDefinitionMap.put("/admin/**","authc");

    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    return shiroFilterFactoryBean;
}

登录控制器验证码部分

@Controller
@RequestMapping("admin/login")
public class LoginController extends ControllerBase {
    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @RequestMapping(value = "captcha")
    public void getCaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            //生产验证码字符串并保存到session中
            String createText = defaultKaptcha.createText();
            httpServletRequest.getSession().setAttribute(KaptchaConfig.SHIRO_VERIFY_SESSION, createText);

            //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
            BufferedImage bufferedImage = defaultKaptcha.createImage(createText);
            ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);

            //定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
            httpServletResponse.setHeader("Cache-Control", "no-store");
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
            httpServletResponse.setContentType("image/jpeg");
            ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
            responseOutputStream.write(byteArrayOutputStream.toByteArray());
            responseOutputStream.flush();
            responseOutputStream.close();
        } catch (IllegalArgumentException ex) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    }
}

前端页面

在适当位置添加如下代码,注意这里input的name值为captcha,在Shiro拦截器器中会用到

<input type="text"
       name="captcha"
       data-required="true"
       data-message="验证码不能为空"
       autocomplete="OFF"
       placeholder="请输入验证码"
/>
<img src="/admin/login/captcha"
	 class="j-col-x j-input-group"
	 style="float: right; width: 120px; height: 32px;"
	 alt="验证码"
	 onclick="this.src='/admin/login/captcha?d='+new Date()*1"
/>

本文地址:https://blog.csdn.net/qq_44977042/article/details/109829388