这里实现验证码校验的思路是自己添加一个Filter继承FormAuthenticationFilter
,FormAuthenticationFilter负责表单验证,shiro会先在FormAuthenticationFilter子类去校验验证码,然后再去做身份验证。
生成验证码这里使用Google的Kaptcha框架。
1.添加依赖
<!--google的验证码框架-->
<dependency>
<groupId>com.google.code.kaptcha</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3</version>
</dependency>
2.拓展UsernamePasswordToken
,将验证码包含进去:
在com.example.demo.config.Shiro包下添加以下类:
//拓展登陆验证字段
public class CaptchaUsernamePasswordToken extends UsernamePasswordToken {
private static final long serivalVersionUID = 1L;
//验证码字符串
private String captcha;
public CaptchaUsernamePasswordToken(String username, char[] password, boolean rememberMe, String host, String captcha) {
super(username,password,rememberMe, host);
this.captcha = captcha;
}
public static long getSerivalVersionUID() {
return serivalVersionUID;
}
public String getCaptcha() {
return captcha;
}
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
}
顺便再Exception包下添加一个验证码异常的类,方便在controller中捕获:
// 校验码异常
public class IncorrectCaptchaException extends AuthenticationException {
private static final long serivalVersionUID = 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);
}
}
2.添加校验用的Filter
public class KaptchaFilter extends FormAuthenticationFilter {
public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
private String captchaParam = DEFAULT_CAPTCHA_PARAM;
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
CaptchaUsernamePasswordToken token = createToken(request, response);
String username = token.getUsername();
try {
doCaptchaValidate((HttpServletRequest) request, token);
Subject subject = getSubject(request, response);
subject.login(token);
return onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
return onLoginFailure(token,e,request,response);
}
}
//验证码jiao校验
protected void doCaptchaValidate(HttpServletRequest request, CaptchaUsernamePasswordToken token) {
// 从session中获取图形吗字符串
String captcha = (String) request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
// 校验
if (captcha == null || !captcha.equals(token.getCaptcha())) {
throw new IncorrectCaptchaException();
}
}
@Override
protected CaptchaUsernamePasswordToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
String host = getHost(request);
boolean rememberMe = isRememberMe(request);
String captcha = getCaptcha(request);
return new CaptchaUsernamePasswordToken(username,password.toCharArray(),rememberMe,host,captcha);
}
protected String getCaptcha(ServletRequest request) {
return WebUtils.getCleanParam(request, getCaptchaParam());
}
//保存异常对象到request
@Override
protected void setFailureAttribute(ServletRequest request, org.apache.shiro.authc.AuthenticationException ae) {
request.setAttribute(getFailureKeyAttribute(), ae);
}
public String getCaptchaParam() {
return captchaParam;
}
public void setCaptchaParam(String captchaParam) {
this.captchaParam = captchaParam;
}
}
在com.example.demo.config.Shiro.ShiroConfiguration
的ShiroFilterFactoryBean中添加验证码filter。
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
.......
//验证码过滤器
Map<String, Filter> filterMap = shiroFilterFactoryBean.getFilters();
KaptchaFilter kaptchaFilter = new KaptchaFilter();
filterMap.put("kaptchaFilter", kaptchaFilter);
shiroFilterFactoryBean.setFilters(filterMap);
....
filterChainDefinitionMap.put("/login", "kaptchaFilter");
filterChainDefinitionMap.put("/kaptcha.jpg", "anon");//图片验证码(kaptcha框架)
....
}
接下来需要在启动类中注册KaptchaServlet.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
// 注册统一异常处理bean
@Bean
public MyExceptionResolver myExceptionResolver() {
return new MyExceptionResolver();
}
//配置kaptcha图片验证码框架提供的Servlet
@Bean
public ServletRegistrationBean kaptchaServlet() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new KaptchaServlet(), "/kaptcha.jpg");
registrationBean.addInitParameter(Constants.KAPTCHA_SESSION_CONFIG_KEY,
Constants.KAPTCHA_SESSION_KEY);
registrationBean.addInitParameter(Constants.KAPTCHA_IMAGE_HEIGHT,"60");//高度
registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE,"50");//字体大小
registrationBean.addInitParameter(Constants.KAPTCHA_BORDER_THICKNESS,"1"); //边框
registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "red"); //文字颜色
//可以设置很多属性,具体看com.google.code.kaptcha.Constants
// kaptcha.border 是否有边框 默认为true 我们可以自己设置yes,no
// kaptcha.border.color 边框颜色 默认为Color.BLACK
// kaptcha.border.thickness 边框粗细度 默认为1
// kaptcha.producer.impl 验证码生成器 默认为DefaultKaptcha
// kaptcha.textproducer.impl 验证码文本生成器 默认为DefaultTextCreator
// kaptcha.textproducer.char.string 验证码文本字符内容范围 默认为abcde2345678gfynmnpwx
// kaptcha.textproducer.char.length 验证码文本字符长度 默认为5
// kaptcha.textproducer.font.names 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
// kaptcha.textproducer.font.size 验证码文本字符大小 默认为40
// kaptcha.textproducer.font.color 验证码文本字符颜色 默认为Color.BLACK
// kaptcha.textproducer.char.space 验证码文本字符间距 默认为2
// kaptcha.noise.impl 验证码噪点生成对象 默认为DefaultNoise
// kaptcha.noise.color 验证码噪点颜色 默认为Color.BLACK
// kaptcha.obscurificator.impl 验证码样式引擎 默认为WaterRipple
// kaptcha.word.impl 验证码文本字符渲染 默认为DefaultWordRenderer
// kaptcha.background.impl 验证码背景生成器 默认为DefaultBackground
// kaptcha.background.clear.from 验证码背景颜色渐进 默认为Color.LIGHT_GRAY
// kaptcha.background.clear.to 验证码背景颜色渐进 默认为Color.WHITE
// kaptcha.image.width 验证码图片宽度 默认为200
// kaptcha.image.height 验证码图片高度 默认为50
return registrationBean;
}
}
3.最后,在login.html页面把验证码添加进去:
<div><label> 验证码 : <input type="text" name="captcha" placeholder="验证码"/> </label></div>
<div>![](@{/kaptcha.jpg})</div>
好了,在进入login登录页面
关于退出登录已经在ShiroFilterFactoryBean中配置过了
filterChainDefinitionMap.put("/logout", "logout");
只需要在页面中访问/logout即可
<p>点击 <a th:href="@{/logout}">退出登录</a></p>
SpringBoot + Shiro (一)基础工程搭建
SpringBoot + Shiro (二)身份校验和角色设置
SpringBoot + Shiro (三)权限
SpringBoot + Shiro (四)缓存&记住密码
SpringBoot + Shiro (五)验证码
最后,感谢几位作者的文章解惑:
springboot整合shiro-登录认证和权限管理
Spring Boot Shiro权限管理【从零开始学Spring Boot】
Spring boot 中使用Shiro
最后帮朋友打个小广告