1.一般实现登录验证码,有两种思路
(1)自定义过滤器
(2)自定义认证逻辑
2.本次通过自定义过滤器实现验证码,之前也写过一篇笔记,是基于自定义认证逻辑实现的,有兴趣的同学可参见:基于SpringBoot+SpringSecurity+VUE实现登录验证码功能-自定义认证逻辑方式(带源码下载)
源码下载链接请见文末。
如果想看SpringSecurity其他笔记,请见:SpringSecurity学习笔记
1.添加kaptcha依赖
<!-- 添加kaptcha依赖 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
2.对kaptcha进行配置
/**
* 对kaptcha进行配置
*/
@Configuration
public class KaptchaConfig {
@Bean
Producer kaptcha(){
Properties properties=new Properties();
// 设置长度
properties.setProperty("kaptcha.image.width","150");
//设置高度
properties.setProperty("kaptcha.image.height","50");
// 设置为数字
properties.setProperty("kaptcha.textproducer.char.string","0123456789");
// 设置验证码为4位
properties.setProperty("kaptcha.textproducer.char.length","4");
Config config=new Config(properties);
DefaultKaptcha defaultKaptcha=new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
3.写一个接口,让登录页面获取生成的验证码
@RestController
public class VertificationCodeController {
@Autowired
Producer producer;
@GetMapping("/vc.jpg")
public void getVerifyCode(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
response.setContentType("image/jpeg");
String text=producer.createText();
// 把生成的验证码保存到session中
session.setAttribute("kaptcha",text);
// 把生成的验证码生成图片
BufferedImage image=producer.createImage(text);
try (ServletOutputStream out=response.getOutputStream()){
ImageIO.write(image,"jpg",out);
}
}
}
4.修改SecurityConfig.java,允许登录页面访问/vc.jpg获取验证码
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/vc.jpg").permitAll()
//此处省略其他
}
5.添加LoginFilter过滤器
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if(!request.getMethod().equals("POST")){
throw new AuthenticationServiceException("非post类型"+request.getMethod());
}
String kaptcha=request.getParameter("kaptcha");
String sessionKaptcha=(String)request.getSession().getAttribute("kaptcha");
if(!StringUtils.isEmpty(kaptcha)&&!StringUtils.isEmpty(sessionKaptcha)&&kaptcha.equals(sessionKaptcha)){
return super.attemptAuthentication(request,response);
}
throw new AuthenticationServiceException("验证码不正确");
}
}
6.在SecurityConfig.java配置验证码校验
@Configuration
@Order(1)
static class SecurityConfig01 extends WebSecurityConfigurerAdapter{
@Autowired
MyUserDetailsService myUserDetailsService;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// 配置验证码有效
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
@Bean
LoginFilter loginFilter() throws Exception{
LoginFilter loginFilter=new LoginFilter();
loginFilter.setFilterProcessesUrl("/doLogin");
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
return loginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//添加过滤器
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
// 此处省略其他
}
// 此处省略其他
}
7.修改前端login登录页面
<template>
<div class="login_container">
<div class="login_bigbox">
<span class="title_span">人事管理系统</span>
<div class="login_box">
<el-form ref="loginFormRef" :model="loginForm" label-width="" class="login_form" :rules="loginFormRules">
<el-form-item label="" prop="username">
<el-input v-model="loginForm.username" prefix-icon="el-icon-user" ></el-input>
</el-form-item>
<el-form-item label="" prop="password">
<el-input v-model="loginForm.password" prefix-icon="el-icon-lock" type="password"></el-input>
</el-form-item>
<el-form-item label="" prop="kaptcha" width="50%">
<el-input v-model="loginForm.kaptcha" prefix-icon="el-icon-lock"></el-input>
</el-form-item>
<el-form-item label="" width="50%"><img :src="validateUrl" alt="" @click="reflashImg"></el-form-item>
<el-form-item class="btns">
<el-button type="primary" @click="login">登录1</el-button>
<el-button type="info" @click="reset">重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script>
import Qs from 'qs'
export default {
data () {
return {
validateUrl: 'http://localhost:1025/vc.jpg',
loginForm: {
username: 'admin',
password: '123',
kaptcha: ''
},
loginFormRules: {
username: [
{ required: true, message: '用户名不能为空', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名必须在3-20个字符之间', trigger: 'blur' }
],
password: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 3, max: 20, message: '密码必须在3-20个字符之间', trigger: 'blur' }
],
kaptcha: [
{ required: true, message: '验证码不能为空', trigger: 'blur' }
]
}
}
},
methods: {
login () {
// sm2加密
// const sm2 = require('sm-crypto').sm2
// const cipherMode = 1
// const pwd = sm2.doEncrypt(this.password, '公钥', cipherMode)
// let sm2 = require('sm-crypto').sm2
// console.log('pwd:' + sm2)
// 先进行表单预验证
this.$refs.loginFormRef.validate(async valid => {
if (valid === true) {
// 表单预验证通过,向后台发送请求
// this.$http.post('login', Qs.stringify(this.loginForm))
this.$http.post('doLogin', Qs.stringify(this.loginForm))
.then(res => {
console.log(res.data)
if (res.data.status === 200) {
const token = res.data.token
// this.$message.success('登录成功')
// 保存token
sessionStorage.setItem('token', token)
this.$router.push('/home')
} else if (res.data.status === 401) {
this.$message.error('登录失败,' + res.data.msg)
} else {
this.$message.error('其他未知错误')
}
})
.catch(error => {
const error2 = error + ''
if (error2.indexOf('401') !== -1) { this.$message.error('用户名或密码错误') } else this.$message.error('外星人把我们的服务器劫走了,刷新一下也许能抢回来哦!')
})
}
})
},
reset () {
this.$refs.loginFormRef.resetFields()
this.$message.success('已重置登录框')
},
reflashImg () {
console.log('hahahdkhfalksdf')
var timstamp2 = (new Date()).valueOf()
this.validateUrl = 'http://localhost:1025/vc.jpg?t=' + timstamp2
}
}
}
</script>
<style lang="less" scoped>
.login_container{
background-color: #2b4b6b;
//background-color: #555;
height: 100%;
}
.login_bigbox{
width: 350px;
height: 450px;
//background-color: #eee;
border-radius: 3px;
position: absolute;
text-align: center;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.title_span{
font-size: 35px;
width: 350px;
font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
position: absolute;
left: 50%;
transform: translate(-50%,-150%);
}
.login_box{
width: 350px;
height: 400px;
background-color: #fff;
border-radius: 3px;
}
.btns{
display:flex;
justify-content: flex-end;
}
.login_form{
position: absolute;
bottom: 15%;
width: 100%;
padding: 10px 20px;
box-sizing: border-box;
}
</style>