前台是vue使用fetch请求后台的登录方法,但是前台浏览器的控制台中的sessionid没有,要么就是跟后台的sessionid不一致,导致后台取验证码的时候是null,因为验证码是后台存在session中
在用fetch进行网络请求的时候,发现每次请求到服务端的时候,他的sessionId 都是不一样的,后面排查原来是在请求的时候fetch默认是不会带上本地jsessionId,以至于服务端无法接收到,所以会重新创建一个新的session。
针对这个问题,完整写法是:主要是
credentials: 'include' 是解决跨域session丢失的问题,加上这一行以后,前端浏览器中的sessionid和后台打印的sessionid是一致的,但是参数传不过来,最后加了一个method:'post',就可以了
let fd=new FormData();
fd.append('username',this.userName);
fd.append('password',this.userPwd);
fd.append('checkCode',this.code);
fetch(`${apiUrl}/login`,{
method:'post',
body:fd,
credentials: 'include'
}).then((res)=>res.json()).
then((res)=>{
console.log(res);
}).catch((res)=>console.log(res));
后台代码
(注:
response.setHeader("Access-Control-Allow-Origin", " http://192.168.1.136:8080"
;);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
response.setHeader("Access-Control-Allow-Credentials", "true");//是否支持cookie跨域
后台什么都不需要加,根本
不需要加跨域这些东西
)
/**
* Created by HONGLINCHEN ON 2017/11/13 9:12
*
验证码
*
@param
request
*
@param
response
*
@return
*/
@GetMapping(value =
"/captchaImage")
public ModelAndView getKaptchaImage(HttpServletRequest request, HttpServletResponse response)
throws Exception {
response.setHeader(
"Access-Control-Allow-Origin", request.getHeader(
"Origin"));
//
禁止服务器端缓存
response.setDateHeader(
"Expires",
0);
//
设置标准的
HTTP/1.1 no-cache headers.
response.setHeader(
"Cache-Control",
"no-store, no-cache, must-revalidate");
//
设置
IE
扩展
HTTP/1.1 no-cache headers (use addHeader).
response.addHeader(
"Cache-Control",
"post-check=0, pre-check=0");
//
设置标准
HTTP/1.0
不缓存图片
response.setHeader(
"Pragma",
"no-cache");
//
返回一个
jpeg
图片,默认是
text/html(
输出文档的
MIMI
类型
)
response.setContentType(
"image/jpeg");
//
为图片创建文本
String capText =
captchaProducer.createText();
//
将文本保存在
session
中,这里就使用包中的静态变量吧
request.getSession().setAttribute(com.google.code.kaptcha.Constants.
KAPTCHA_SESSION_KEY, capText);
//
创建带有文本的图片
BufferedImage bi =
captchaProducer.createImage(capText);
ServletOutputStream out = response.getOutputStream();
//
图片数据输出
ImageIO.
write(bi,
"jpg", out);
String kaptchaExpected = (String) request.getSession().getAttribute(Constants.
KAPTCHA_SESSION_KEY);
System.
err.println(
"
登录的验证码是:
"+kaptchaExpected);
try {
out.flush();
}
finally {
out.close();
}
return null;
}
/**
*
验证验证码
*
@param
checkCode
*
@return
正确
:true/
错误
:false
*/
public static boolean validate(String checkCode)
throws Exception {
//
获取
Session
中验证码
Object captcha = RequestUtils.
getAttribute(com.google.code.kaptcha.Constants.
KAPTCHA_SESSION_KEY);
if(captcha==
null){
throw new Exception(UserReturnCodeEnum.
INCALID_VALIDATION_CODE.getMessage());
}
if (StringUtils.
isEmpty(checkCode)) {
return false;
}
boolean result = checkCode.equalsIgnoreCase(captcha.toString());
if (result) {
RequestUtils.
getRequest().getSession().removeAttribute(com.google.code.kaptcha.Constants.
KAPTCHA_SESSION_KEY);
}
return result;
}
/**
* Created by HONGLINCHEN ON 2017/11/13 9:12
*
用户登录
*
@param
username
*
@param
password
*
@return
*/
@PostMapping(
"login")
@ResponseBody
public Object login(
@RequestParam(
"username")String username,
@RequestParam(
"password")String password,
@RequestParam(
"checkCode")String checkCode,HttpServletRequest request,HttpServletResponse response)
throws Exception {
if (!SingletonLoginUtils.
validate(checkCode)) {
return new GmfResult(UserReturnCodeEnum.
VERIFICATION_ERROR);
}
Subject currentUser = SecurityUtils.
getSubject();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token =
new UsernamePasswordToken(username, password);
token.setRememberMe(
true);
//
默认不记住密码
try {
currentUser.login(token);
GmfUsersLoginLog usersLoginLog =
new GmfUsersLoginLog(SingletonLoginUtils.
getUserId(),
new Date(), RequestUtils.
getIpAddr(RequestUtils.
getRequest()),RequestUtils.
getUserOperatingSystem(),RequestUtils.
getUserBrowser());
Integer count =
gmfUsersLoginLogService.updateGmfUserLoginLogByUserId(SingletonLoginUtils.
getUser(),usersLoginLog);
if (count>
0){
request.getSession().setAttribute(GmfUsersGlobal.
USER_SESSION_KEY, currentUser.getPrincipal());
return ResultUtils.
successLogin(ResultEnum.
LOGIN_SUCCESS);
}
else {
return ResultUtils.
error(ResultEnum.
UNKNOW_ERROR.getCode(),ResultEnum.
UNKNOW_ERROR.getMsg());
}
}
catch (UnknownAccountException uae) {
logger.error(username+UserReturnCodeEnum.
USER_NOT_EXIST.getMessage());
logger.info(
"There is no user with username of " + token.getPrincipal());
return new GmfResult(UserReturnCodeEnum.
USER_NOT_EXIST);
}
catch (IncorrectCredentialsException ice) {
logger.error(username+UserReturnCodeEnum.
WRONG_PASSWORD.getMessage());
logger.info(
"Password for account " + token.getPrincipal() +
" was incorrect!");
return new GmfResult(UserReturnCodeEnum.
WRONG_PASSWORD);
}
catch (ExcessiveAttemptsException e) {
//
登录失败多次,账户锁定
5
分钟
logger.error(UserReturnCodeEnum.
ACCOUNT_LOCK.getMessage());
return new GmfResult(UserReturnCodeEnum.
ACCOUNT_LOCK);
}
catch (LockedAccountException lae) {
logger.error(UserReturnCodeEnum.
USER_SUSPEND.getMessage());
return new GmfResult(UserReturnCodeEnum.
USER_SUSPEND);
}
catch (DisabledAccountException dae) {
logger.error(UserReturnCodeEnum.
USER_DISABLE.getMessage());
return new GmfResult(UserReturnCodeEnum.
USER_DISABLE);
}
catch (AuthenticationException ae) {
return new Exception(
"
登录失败!
");
}
catch (RuntimeException e) {
logger.error(UserReturnCodeEnum.
UNKNOWN_ERROR.getMessage());
return new GmfResult(UserReturnCodeEnum.
UNKNOWN_ERROR);
}
}
else {
return ResultUtils.
successLogin(ResultEnum.
LOGIN_SUCCESS);
}
}
完结
说明:当时参考这个( https://www.cnblogs.com/neverleave/p/6533133.html)文章,但是我们前台加入
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
,
},是不行的 参数传递不到后台,最后不加解决
javascript 使用fetch进行跨域请求时默认是不带cookie的,所以会造成 session失效。
fetch(url, {
method:
'POST'
,
credentials:
'include'
,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
,
},
body: JSON.stringify({
data: options.data
})
})
credentials: 'include' 可以是fetch 带上cookie。但是问题了来。
原来在服务器端设置header (php 服务器)
header
("Access-Control-Allow-Origin: *");
会报错:
A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin ' http://localhost:8000'
; is therefore not allowed access.
可以看到不允许 使用‘*’ 号了,那么就改成 访问域名(这里是本地调用所以是 http://localhost:8000
)
header("Access-Control-Allow-Origin: http://localhost:8000"
;);
改完后再次发送请求,还是报错
Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin ' http://localhost:8000'
; is therefore not allowed access.
说'Access-Control-Allow-Credentials 头必须是true,那么继续增加
header
("Access-Control-Allow-Credentials: true");
增加完后可以正常访问了,而且session也有了。
ps: fetch 有个mode 是no-cors ,发现设置后返回的status是0,查资料后
no-cors
mode is only to CDN content, such as scripts, CSS and image, you cannot be used for getting data,
response.status = 0
is right behavior
no-cors 模式只能用来获取CDN内容,比如脚本,css文件和图片,如果用来获取数据比如json格式就会返回status=0
还参考了这篇资料:
http://blog.csdn.net/codetomylaw/article/details/52588493
《React-Native系列》31、 Fetch发送POST请求的坑与解决方案
我们在请求http接口时候,通常都会使用get和post的方式,针对表单提交这类的请求,我们通常采用post方式。
那么在RN中的Fetch API中post提交有哪些坑呢?让我们撸起来。
我们先来说说Server端的代码,通常我们从Request获取参数时的方法为:
- String paraValue = request.getParameter(paraName);
我们下面说的判断能不能获取参数,就是按照这种方法来获取。
在 RN中,通常我们会怎么写代码呢?
方案一(不推荐)
:
[javascript]
view plain
copy
- let url = "http://127.0.0.1:8080/api/testFetch”
- let params = "name=admin&password=admin123”;
- fetch(url , {
- method: 'POST',
- headers: {},
- body: params,
- }).then((response) => {
- if (response.ok) {
- return response.json();
- }
- }).then((json) => {
- alert(JSON.stringify(json));
- }).catch((error) => {
- console.error(error);
- });
此时我们发现在Server端无法获取到name和password的值。
换成GET试试,将params追加到url后,发现ok。那这是什么情况呢?下面讲解。
好,不行,我们就再换一种方法试试呗。
方案二(不推荐):
[javascript]
view plain
copy
- let params = {"name":"admin","password":"admin123"};
-
- fetch(url , {
- method: 'POST',
- headers: {},
- body:JSON.stringify(params),
- }).then((response) => {
- if (response.ok) {
- return response.json();
- }
- }).then((json) => {
- alert(JSON.stringify(json));
- }).catch((error) => {
- console.error(error);
- });
我们直接将params封装成一个JSON对象,然后在body里将JSON对象转成字符串传过去,发现然并卵,Server端还是获取不到值。
好,我们不兜圈子了,直接说明原因。
其实,方案一和方案二都是直接在body里传递了一个字符串,在Server端获取body的方式如下:
- StringBuilder buffer = new StringBuilder();
- BufferedReader reader = beat.getRequest().getReader();
- String line;
- while ((line = reader.readLine()) != null) {
- buffer.append(line);
- }
- String body = buffer.toString();
通过这种方法我们可以获取到传入的字符串。
既然能获取到字符串,那么我们也可以拿到我们传入的值了,可以转JSON、或者按&切割字符串,只不过这种解决方案好像有点挫啊!!!
也许你会问在jquery中,我们就是按照方案一这种方式做的啊,怎么好使呢?
答:因为参数 "name=admin&password=admin123” 在jquery中,传入对象框架会自动封装成formData的形式,fetch没有这个功能。
终极方案(推荐使用):
既然fetch不会自动转FormData,那我们自己new一个FormData,直接传给body。
在FormData中也可以传递字节流实现上传图片的功能。参考: http://blog.csdn.net/codetomylaw/article/details/52446786
[javascript]
view plain
copy
- let formData = new FormData();
- formData.append("name","admin");
- formData.append("password","admin123");
-
- etch(url , {
- method: 'POST',
- headers: {},
- body: formData,
- ).then((response) => {
- if (response.ok) {
- return response.json();
- }
- ).then((json) => {
- alert(JSON.stringify(json));
- ).catch((error) => {
- console.error(error);
- );
这样我们就可以在Server端获取到name和password的值了。