web 部署专题(九):Nginx 前后端分离中csrf_token 认证的实现

1. 思路

参考:https://stackoverflow.com/questions/20826201/simple-csrf-protection-using-nginx-alone?r=SearchResults

第一步:

       前端页面向后端发送生成csrf请求(get 方法),后端服务器生成csrf_token返回gei前端

第二步:

      前端收到csrf_token后,将csrf_token写入cookie中,在post请求中,随cookie与请求头发送到后端中。

2.相关代码

2.1 Nginx 配置

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#
#server {
#    listen 80 default_server;
#    listen [::]:80 default_server;

    # SSL configuration
    #
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server;
    #
    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

#    root /var/www/html;

    # Add index.php to the list if you are using PHP
#    index index.html index.htm index.nginx-debian.html;

#    server_name _;

#    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
#        try_files $uri $uri/ =404;
#    }

    # pass PHP scripts to FastCGI server
    #
    #location ~ \.php$ {
    #    include snippets/fastcgi-php.conf;
    #
    #    # With php-fpm (or other unix sockets):
    #    fastcgi_pass unix:/run/php/php7.3-fpm.sock;
    #    # With php-cgi (or other tcp sockets):
    #    fastcgi_pass 127.0.0.1:9000;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny all;
    #}
#}


# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
server {
    listen 80;
    listen [::]:80;
#
    server_name localhost;
#
    #root /var/www/example.com;
#    root /var/www/html;

#    root /home/pi/Desktop/tornado_example/linux_tornado/movie/static;
#    index index.html;
#
    location ~* /admin/ {
#        try_files $uri $uri/ =404;
                proxy_pass http://127.0.0.1:5000;
#                proxy_connect_timeout 600;
#                proxy_read_timeout 600;
                proxy_cookie_domain localhost:5000 localhost:80;   #保证cookie不受跨域影响    
    }


    location / {
             root /var/www/;
             index index.nginx-debian.html;

    }

    location /html/ {
             root /var/www/static/;
             index index.nginx-debian.html;

    }
}

2.1 flask 后端编写

from flask_wtf import csrf

@admin.route("/csrf_token")
def get_csrf_token():
    "生成csrf_token"
    csrf_token = csrf.generate_csrf()
    return '{"errno":0,"errmsg":"OK","csrf_token":"%s"}' % csrf_token,200,{"Content-Type":"application/json"} 

2.2 前端编写

js 文件

login.js

function getCookie(name) {
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

function setCookie() {

    $.get("/admin/csrf_token",function(resp) {  // 向后端发送获取csrf_token 请求
     
      $("#csrf_token").attr("csrf_token",resp.csrf_token) // 将得到的 csrf_token写道html元素中
    });

}




$(document).ready(function() {
    $("#user").focus(function(){
        $("#user-err").hide();
    });
    $("#password").focus(function(){
        $("#password-err").hide();
    });


   setCookie() //获取csrf_token



    $(".form-login").submit(function(e){
        e.preventDefault();
        account = $("#user").val();
        passwd = $("#password").val();
        if (!account) {
            $("#user-err span").html("请填写正确帐号!");
            $("#user-err").show();
            return;
        } 
        if (!passwd) {
            $("#password-err span").html("请填写密码!");
            $("#password-err").show();
            return;
        }
        var data = {
            account:account,
            password:passwd
        };


        var csrf_data = $("#csrf_token").attr("csrf_token");  // 从html 获取csrf_token

        document.cookie = "csrf_token" + "=" + csrf_data;  // 将csrf_token 写入cookie




        var jsonData = JSON.stringify(data);
        $.ajax({  // post 请求登录
            url:"/admin/sessions", 
            type:"post",
            data:jsonData,
            contentType:"application/json",
            dataType:"json",
            headers:{
                "X-CSRFToken":getCookie("csrf_token")
            },
            success:function (data) {
                if (data.errno=="0"){
                    // 登录成功,跳转到主页
                    location.href ="/html/index.html"
                } else {
                  // 其他错误信息,在页面中展示
                  $("#password-err span").html(data.errmsg);
                  $("#password-err").show();
                }
            }
        });
    });
})

 

 

 

 

html 文件

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>招投标爬虫管理</title>
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">

    <link rel="stylesheet" href="../static/admin/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="../static/fonts/css/font-awesome.min.css">
    <link rel="stylesheet" href="../static/ionicons/css/ionicons.min.css">
    <link rel="stylesheet" href="../static/admin/dist/css/AdminLTE.min.css">
    <link rel="stylesheet" href="../static/admin/plugins/iCheck/square/blue.css">
</head>
<body class="hold-transition login-page">
<div class="login-box">
    <div class="login-logo">
        <a href=""><b>招投标爬虫系统</b></a>
    </div>
    <div class="login-box-body">
        <p class="login-box-msg"></p>
        <form class="form-login">
            <div class="form-group has-feedback">
                <input name="user" id="user" type="text" class="form-control" placeholder="请输入账号!">
                <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
                <div class="col-md-12" id="input_user"></div>
            </div>
            <div class="error-msg" id="user-err"><i class="fa fa-exclamation-circle"></i><span></span></div>
            <div class="form-group has-feedback">
                <input name="pwd" id="password" type="password" class="form-control" placeholder="请输入密码!">
                <span class="glyphicon glyphicon-lock form-control-feedback"></span>
                <div class="col-md-12" id="input_pwd"></div>
            </div>
       <div class="error-msg" id="password-err"><i class="fa fa-exclamation-circle"></i><span></span></div>
            <div class="form-group has-feedback">
                <input name="csrf_token" id="csrf_token" type="hidden" class="form-control" >
            </div>

           <div class="row">
                <div class="col-xs-8"></div>
                <div class="col-xs-4">
            <input id="btn-sub" type="submit" class="btn btn-primary btn-block btn-flat" value="登录">
                </div>
            </div>
        </form>
    </div>
</div>
<script src="../static/admin/plugins/jQuery/jQuery-2.2.0.min.js"></script>
<script src="../static/admin/bootstrap/js/bootstrap.min.js"></script>
<script src="../static/admin/plugins/iCheck/icheck.min.js"></script>

<script src="/static/js/crawler/login.js"></script>


</body>
</html>

 

posted @ 2020-04-25 15:50  秋华  阅读(3430)  评论(0编辑  收藏  举报