说明:以SpringBoot为后端,React和Fetch为前端,举例说明。
零:三条军规:
以后的论述都是为绕过军规的手段:
- 军规一: 浏览器环境中无法利用JS 获取跨域后端的 Set-Cookie响应头
- 军规二: 浏览器环境中无法使用直接使用JS异步发送Cookie请求头
- 军规三:浏览器环境中,默认不可以使用JS访问document.cookie对象
一、 服务器端的设置:
必须在跨域设置中加入:
- 设置响应头
Access-Control-Allow-Credentials: true
, 使XHR引擎可以访问到document.cookie。 - 设置响应头:
Access-Control-Allow-Origin: http://localhost:3001
,需要指定前端的域,不能使用默认的 ' * ' - 取消cookie的httpOnly属性,使JS可以访问到cookie:
application.yml
server:
servlet:
session:
cookie:
http-only: false
以上设置必然是以降低安全性为代价
二、客户端设置:
- fetch 中加入
{credentials: 'include'}
- xhr和axios中加入:
xhr.withCredentials = true;
和{withCredentials:true}
此举可以在异步请求中,把document.cookie中的信息,以Cookie头的形式发出 - jquery中加入:
$.ajax({
url: "http://localhost:8080/test1",
crossDomain: true,
xhrFields: {
withCredentials: true
}
});
三、客户端代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Add React in One Minute</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
//1. 登录成功,服务器端返回Set-Cookie: <Session ID>,会以JSESSIONID=XXX存入document.cookie中,
//2. credentials: 'include' 的第一个含义:允许JS 访问document.cookie,但注意服务器两个条件的配合:
// (1)Access-Control-Allow-Credentials:true ,Access-Control-Allow-Origin: <不为*>
// (2)取消cookie中的 http-only属性
_login=()=>{
fetch('http://localhost:8080/login?uname=john&password=123',
{credentials: 'include'})
.then(resp=>resp.text())
.then(info=>alert(info))
}
//1. 这是登录成功后的请求,应用了credentials: 'include'的第二个含义,即:从document.cookie中获取信息,加入请求头中Cookie: <XXX>
//2. 此处仍然需要服务器的两个条件配合
_info=()=>{
fetch('http://localhost:8080/info',
{method:'GET',credentials: 'include'})
.then(resp=>resp.text())
.then(info=>alert(info))
}
render() {
return (
<div>
<button onClick={this._login}>登录</button>
<button onClick={this._info}>
获取信息
</button>
</div>
)
}
}
ReactDOM.render(<App/>, document.querySelector('#app'));
</script>
</body>
</html>
四、服务器端代码:
package sso;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.Optional;
@RestController
@CrossOrigin(allowCredentials = "true",origins = "http://localhost:3001")
public class SsoController {
@RequestMapping("/login")
public String login(String uname, String password, HttpSession session){
if("john".equals(uname) && "123".equals(password)){
session.setAttribute("username",uname);
return "suc";
}
else {
return "fail";
}
}
@RequestMapping("/info")
public String info(HttpSession session){
String info= Optional.
ofNullable((String)session.getAttribute("username"))
.orElse("fail");
return info;
}
}
网友评论