最近做项目时使用到了通过验证码图片code参数实现登录验证的技术,这里记录一下,以供参考
先定义一个工具类,用于生成验证码图片
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
/**
* @author 阿楠
* code生成工具类
*/
public class CodeUtils {
/**
* 生成验证码图片的宽度
*/
private int width = 100;
/**
* 生成验证码图片的高度
*/
private int height = 30;
/**
* 字符样式
*/
private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };
/**
* 定义验证码图片的背景颜色为白色
*/
private Color bgColor = new Color(255, 255, 255);
/**
* 生成随机
*/
private Random random = new Random();
/**
* 定义code字符
*/
private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* 记录随机字符串
*/
private String text;
/**
* 获取一个随意颜色
* @return
*/
private Color randomColor() {
int red = random.nextInt(150);
int green = random.nextInt(150);
int blue = random.nextInt(150);
return new Color(red, green, blue);
}
/**
* 获取一个随机字体
*
* @return
*/
private Font randomFont() {
String name = fontNames[random.nextInt(fontNames.length)];
int style = random.nextInt(4);
int size = random.nextInt(5) + 24;
return new Font(name, style, size);
}
/**
* 获取一个随机字符
*
* @return
*/
private char randomChar() {
return codes.charAt(random.nextInt(codes.length()));
}
/**
* 创建一个空白的BufferedImage对象
*
* @return
*/
private BufferedImage createImage() {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) image.getGraphics();
//设置验证码图片的背景颜色
g2.setColor(bgColor);
g2.fillRect(0, 0, width, height);
return image;
}
public BufferedImage getImage() {
BufferedImage image = createImage();
Graphics2D g2 = (Graphics2D) image.getGraphics();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 4; i++) {
String s = randomChar() + "";
sb.append(s);
g2.setColor(randomColor());
g2.setFont(randomFont());
float x = i * width * 1.0f / 4;
g2.drawString(s, x, height - 8);
}
this.text = sb.toString();
drawLine(image);
return image;
}
/**
* 绘制干扰线
*
* @param image
*/
private void drawLine(BufferedImage image) {
Graphics2D g2 = (Graphics2D) image.getGraphics();
int num = 5;
for (int i = 0; i < num; i++) {
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
g2.setColor(randomColor());
g2.setStroke(new BasicStroke(1.5f));
g2.drawLine(x1, y1, x2, y2);
}
}
public String getText() {
return text;
}
public static void output(BufferedImage image, OutputStream out) throws IOException {
ImageIO.write(image, "JPEG", out);
}
}
在前端调用可生成验证码图片,同时将验证码的code存在session中,用于登录判断时的验证
/**
* 生成验证码图片
* @param request
* @param res
* @throws IOException
*/
@GetMapping("/code")
public void code(HttpServletRequest request,
HttpServletResponse res) throws IOException {
CodeUtils code = new CodeUtils();
BufferedImage image = code.getImage();
String text = code.getText();
HttpSession session = request.getSession(true);
session.setAttribute("code", text);
CodeUtils.output(image,res.getOutputStream());
}
登录接口这里同时也使用了token,仅使用code验证可以直接看注释
//判断code是否正确,之后的代码,使用if判断code错误时的输出,当session里没有code或者前端传递来的code参数跟后端session里的code参数不一致的时候,可判断验证码错误
注意:①||后面的!表示非②toUpperCase()用于不区分字符大小写
/**
* 登录实现token,并验证code
* @param param
* @param request
* @return
*/
@PostMapping(value = "/login")
public Object login(@RequestBody Map<String,String> param,
HttpServletRequest request,
HttpServletResponse response){
JSONObject jsonObject = new JSONObject();
String root_name = param.get("root_name");
String root_password = param.get("root_password");
Root root = rootService.checkRoot(root_name,root_password);
//判断code是否正确
if(request.getSession(true)
.getAttribute("code")==null
||
!param.get("code").toUpperCase()
.equals(request.getSession(true)
.getAttribute("code")
.toString().toUpperCase())){
jsonObject.put("message", "code error!");
return jsonObject;
}
//登录判断
else if (root == null){
jsonObject.put("message", "login error!");
}else {
//登陆成功利用session存储账号密码
HttpSession session =request.getSession(true);
session.setAttribute("root",root);
String token = rootService.getToken(root);
jsonObject.put("message", "login success!");
jsonObject.put("token", token);
jsonObject.put("root_img", root.getRoot_img());
Cookie cookie = new Cookie("token", token);
cookie.setPath("/");
response.addCookie(cookie);
}
return jsonObject;
}
注意因为是前后端分离开发,每次前端的ajax请求都是新的,每次请求的sessionID都不一样,所以每次使用的session不是同一个,以至于登录时session里面的值为null。
需要在vue项目的main.js中配置axios.defaults.withCredentials为true,使得每次请求都带上浏览器的cookie,这样后端使用的session就是同一个了。
axios.defaults.withCredentials = true;
在登录框中定义验证码图片
<el-form-item prop="code">
<span><i class="iconfont icon-icon_anquan" style="font-size: 25px">i>span>
<el-input type="text" auto-complete="false"
v-model="loginForm.code" placeholder="点击图片更换验证码"
style="width: 60%;margin-left: 10px"
@keyup.enter.native="submitLogin('loginForm')">el-input>
<el-image class="codeImg" :src="imgUrl" @click="resetImg">el-image>
el-form-item>
这里定义一个验证码图片样式
.codeImg {
margin-top: 5px;
float: right;
}
在data参数中定义对应的imgUrl,new Date()生成随机事件用于更换验证码
在提交表单里面定义一个code参数用于传参
imgUrl:'http://localhost:8181/root/code?time='+new Date(),
loginForm: {
root_name: 'anan',
root_password: '123456',
code:''
},
定义点击更换验证码图片的方法resetImg(),因为imgUrl本身就是随机,所以这里直接重新随机一个imgUrl实现验证码图片的刷新
resetImg(){
this.imgUrl = "http://localhost:8181/root/code?time="+new Date();
}
这里可以稍微的测试一下生成图片中的code参数
刷新一下登录页面,查看生成的code参数,生成成功,我们就可以接下去做登录验证啦!
这里附上提交登录表单的接口请求,传递code参数给后端验证实现登录
如果res.data.message为code error!,表示code验证错误,扔出message提示验证码错误,并重置imgUrl刷新验证码图片
_this.axios.post('login后端接口',
{root_name: this.loginForm.root_name,
root_password: this.loginForm.root_password,
code: this.loginForm.code},
{'Content-Type': 'application/json; charset=utf-8'}
).then(res => {
console.log(res);
if (res.data.message === 'code error!'){
_this.$message.error('验证码错误!');
_this.imgUrl=
"http://localhost:8181/root/code?time="
+new Date();}
else if (res.data.message === 'login error!'){
_this.$message
.error('用户名或密码错误,请重新登录!');}
else {
//存session
sessionStorage.setItem('root_name', _this.loginForm.root_name);
sessionStorage.setItem('root_password', _this.loginForm.root_password);
sessionStorage.setItem('root_img', res.data.root_img);
sessionStorage.setItem("root_token",res.data.token);
_this.$message({
message: '登录成功!',
type: 'success',
});
_this.$router.push({path:'/home'});}
}