前后端分离方式下,跨域使用cookie进行安全认证

2021-01-20  本文已影响0人  JohnYuCN

说明:以SpringBoot为后端,React和Fetch为前端,举例说明。

零:三条军规:

以后的论述都是为绕过军规的手段:

一、 服务器端的设置:

必须在跨域设置中加入:

  1. 设置响应头 Access-Control-Allow-Credentials: true, 使XHR引擎可以访问到document.cookie。
  2. 设置响应头: Access-Control-Allow-Origin: http://localhost:3001,需要指定前端的域,不能使用默认的 ' * '
  3. 取消cookie的httpOnly属性,使JS可以访问到cookie:
    application.yml
server:
  servlet:
    session:
      cookie:
        http-only: false

以上设置必然是以降低安全性为代价

二、客户端设置:

  1. fetch 中加入{credentials: 'include'}
  2. xhr和axios中加入: xhr.withCredentials = true;{withCredentials:true}
    此举可以在异步请求中,把document.cookie中的信息,以Cookie头的形式发出
  3. 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;
    }
}
上一篇下一篇

猜你喜欢

热点阅读