问题
最近在给项目开发登录注册功能,在登录的图片验证码上出现了问题,报了空指针异常。
逻辑是这样的:在登录页面,前端通过验证码接口先请求验证码,然后输入用户名密码和验证码之后,请求登录接口。在验证码接口中我用session保存验证码,在登录接口中我从session取出验证码进行校验。 postman测试没有问题,但是前端在测试时报了空指针异常。
于是我查看后端日志,发现请求验证码时返回给前端的sessionId和前端发送登录请求时所携带的sessionId不一致,所以后端拿到的sessionId是没有对应的验证码的,于是报了空指针异常。
通过查阅网上的各种信息,我们了解到这是因为跨域问题导致的,跨域我是配置过的,按照网上的办法前后端都配置了相应的跨域设置,但是依然没有解决问题。
具体可以参考链接试试,也是有人成功的:https://www.cnblogs.com/jpfss/p/9081570.html
解决办法
这个问题困扰了我们好几天,期间也是尝试过很多方法,无论是前端的跨域配置,还是后端的跨域配置都试过了,但是问题还是没有解决,不知道是不是因为我将shiro整合JWT的时候关闭了shiro的session导致的,并不清楚,我把shiro的session打开后也没有解决问题。
既然跨域这条路走不通,我们就换条路走,下面给出两种解决方法
方法一:不使用session
不使用session来实现验证码的验证,将验证码转存到redis或者数据库中,前端在通过验证码接口获取验证码时除了返回验证码本身,还要返回验证码的id,那么前端在登录的时候也要把验证码id和用户输入的验证码传给后端进行验证。这个方法并没有使用session,所以并不重点展开,我主要介绍第二种方法。
方法二:取消验证码接口,只保留登录接口来实现获取图片验证码和登录功能
使用session时对同一个页面多次刷新是不会改变sessionid的
既然sessionid不会改变,也就解决了我们之前的问题,那么新的问题是如何只通过一个接口实现即能返回验证码,又能实现登录功能呢。
我在开发登录接口时,会在登录失败时返回code为400的错误信息,在登录成功时返回code为200,也就是说只要前端发送的请求中用户名,密码,验证码任意一个为空,后端都会返回错误信息,那么只要在返回错误信息的同时将验证码的base64格式传给前端就可以了。
@PostMapping("login")publicResultlogin(@RequestBodyUser loginUser,HttpServletResponse response,HttpServletRequest request)throwsIOException{//检验loginUser元素是否完整,不完整则返回错误信息String message=newJudge().judgeUser(loginUser);if(message!="none"){//这是我封装的判断方法,如果message不为none的话,说明登录信息不完整,此时就可以返回验证码了//生成验证码String code=VerifyCodeUtils.generateVerifyCode(4);//验证码放入session
request.getSession().setAttribute("code",code);System.out.println("getImageSession:"+request.getSession().getId());//验证码存入图片ByteArrayOutputStream bs=newByteArrayOutputStream();VerifyCodeUtils.outputImage(110,30,bs,code);//将图片转成二进制并进行Base64编码String imgsrc=Base64.byteArrayToBase64(bs.toByteArray());System.out.println(imgsrc);returnResult.fail(message,"data:image/png;base64,"+imgsrc);}//验证图片验证码String codes=(String)request.getSession().getAttribute("code");System.out.println("loginSession:"+request.getSession().getId());System.out.println("codes:"+codes);System.out.println("loginUser.getCode():"+loginUser.getCode());try{if(!codes.equalsIgnoreCase(loginUser.getCode())){//equalsIgnoreCase忽略大小写returnResult.fail("验证码错误");}}catch(NullPointerException e){
e.printStackTrace();returnResult.fail("空指针异常");}//后面就是基本的登录逻辑了,和本问题无关,所以就不贴出来了。}
那么前端要做的事也就很简单了,在用户访问登录界面时发送一个空请求给后端,从而拿到验证码图片的base64格式,再加上img src标签后就可以展示图片了,用户填写完整的登录信息后再给后端发送请求就能实现登录了。
附上前端代码:
一、在点击用户名输入框的时候 发送一个请求获取验证码。
$('.inputName').one("focus",function(){
$.ajax({
url:'http://*.*.*.*/user/login',
type:'POST',
contentType:"application/json",
dataType:'json',//json 返回值类型
data:JSON.stringify({
username:""+ rt.value,
password:""+ uu.value,
code:""+ op.value}),//转化为json字符串
xhrFields:{
withCredentials:true},success:function(datas){$('.pp').attr("src", datas.data);}})})
二、点击登录的时候 将信息提交给服务的接口
tt[3].onclick=function(){
ru.style.display='block';
$.ajax({
url:'http://*.*.*.*/user/login',
type:'POST',
contentType:"application/json",
dataType:'json',//json 返回值类型
data:JSON.stringify({
username:""+ rt.value,
password:""+ uu.value,
code:""+ op.value}),//转化为json字符串
xhrFields:{
withCredentials:true},success:function(datas){
console.log(datas);if(datas.code=='200'){
ru.style.display='none';
ty.innerHTML= datas.msg;
ty.style.display='block';
tt[3].setAttribute("class","sub");alert('好兄弟即将起航');var seconds=3;setInterval(function(){
seconds--;if(seconds==0){
window.location.href="../main_page.html";}},1000);}else{
ru.style.display='none';
ty.innerHTML= datas.msg;
ty.style.display='block';}}});}
最后自然是成功解决了验证码报空指针的问题了