极验使用详情见官网:https://docs.geetest.com/install/deploy/server/python

后端 Django 使用极验方式:

1.准备好3个文件,并放在对应位置:

# !/usr/bin/env python
# -*- coding:utf-8 -*-

import sys
import random
import json
import requests
import time
from hashlib import md5

if sys.version_info >= (3,):
    xrange = range

VERSION = "3.2.0"


class GeeTestLib(object):
    FN_CHALLENGE = "geetest_challenge"
    FN_VALIDATE = "geetest_validate"
    FN_SECCODE = "geetest_seccode"

    GT_STATUS_SESSION_KEY = "gt_server_status"

    API_URL = "http://api.geetest.com"
    REGISTER_HANDLER = "/register.php"
    VALIDATE_HANDLER = "/validate.php"

    def __init__(self, captcha_id, private_key):
        self.private_key = private_key
        self.captcha_id = captcha_id
        self.sdk_version = VERSION
        self._response_str = ""

    def pre_process(self, user_id=None):
        """
        验证初始化预处理.
        """
        status, challenge = self._register(user_id)
        self._response_str = self._make_response_format(status, challenge)
        return status

    def _register(self, user_id=None):
        challenge = self._register_challenge(user_id)
        if len(challenge) == 32:
            challenge = self._md5_encode("".join([challenge, self.private_key]))
            return 1, challenge
        else:
            return 0, self._make_fail_challenge()

    def get_response_str(self):
        return self._response_str

    def _make_fail_challenge(self):
        rnd1 = random.randint(0, 99)
        rnd2 = random.randint(0, 99)
        md5_str1 = self._md5_encode(str(rnd1))
        md5_str2 = self._md5_encode(str(rnd2))
        challenge = md5_str1 + md5_str2[0:2]
        return challenge

    def _make_response_format(self, success=1, challenge=None):
        if not challenge:
            challenge = self._make_fail_challenge()
        string_format = json.dumps(
            {'success': success, 'gt': self.captcha_id, 'challenge': challenge})
        return string_format

    def _register_challenge(self, user_id=None):
        if user_id:
            register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}".format(
                api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id)
        else:
            register_url = "{api_url}{handler}?gt={captcha_ID}".format(
                api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id)
        try:
            response = requests.get(register_url, timeout=2)
            if response.status_code == requests.codes.ok:
                res_string = response.text
            else:
                res_string = ""
        except:
            res_string = ""
        return res_string

    def success_validate(self, challenge, validate, seccode, user_id=None, gt=None, data='', userinfo=''):
        """
        正常模式的二次验证方式.向geetest server 请求验证结果.
        """
        if not self._check_para(challenge, validate, seccode):
            return 0
        if not self._check_result(challenge, validate):
            return 0
        validate_url = "{api_url}{handler}".format(
            api_url=self.API_URL, handler=self.VALIDATE_HANDLER)
        query = {
            "seccode": seccode,
            "sdk": ''.join(["python_", self.sdk_version]),
            "user_id": user_id,
            "data": data,
            "timestamp": time.time(),
            "challenge": challenge,
            "userinfo": userinfo,
            "captchaid": gt
        }
        backinfo = self._post_values(validate_url, query)
        if backinfo == self._md5_encode(seccode):
            return 1
        else:
            return 0

    def _post_values(self, apiserver, data):
        response = requests.post(apiserver, data)
        return response.text

    def _check_result(self, origin, validate):
        encodeStr = self._md5_encode(self.private_key + "geetest" + origin)
        if validate == encodeStr:
            return True
        else:
            return False

    def failback_validate(self, challenge, validate, seccode):
        """
        failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
        """
        if not self._check_para(challenge, validate, seccode):
            return 0
        validate_str = validate.split('_')
        encode_ans = validate_str[0]
        encode_fbii = validate_str[1]
        encode_igi = validate_str[2]
        decode_ans = self._decode_response(challenge, encode_ans)
        decode_fbii = self._decode_response(challenge, encode_fbii)
        decode_igi = self._decode_response(challenge, encode_igi)
        validate_result = self._validate_fail_image(
            decode_ans, decode_fbii, decode_igi)
        return validate_result

    def _check_para(self, challenge, validate, seccode):
        return (bool(challenge.strip()) and bool(validate.strip()) and bool(seccode.strip()))

    def _validate_fail_image(self, ans, full_bg_index, img_grp_index):
        thread = 3
        full_bg_name = str(self._md5_encode(str(full_bg_index)))[0:10]
        bg_name = str(self._md5_encode(str(img_grp_index)))[10:20]
        answer_decode = ""
        for i in range(0, 9):
            if i % 2 == 0:
                answer_decode += full_bg_name[i]
            elif i % 2 == 1:
                answer_decode += bg_name[i]
        x_decode = answer_decode[4:]
        x_int = int(x_decode, 16)
        result = x_int % 200
        if result < 40:
            result = 40
        if abs(ans - result) < thread:
            return 1
        else:
            return 0

    def _md5_encode(self, values):
        if type(values) == str:
            values = values.encode()
        m = md5(values)
        return m.hexdigest()

    def _decode_rand_base(self, challenge):
        str_base = challenge[32:]
        i = 0
        temp_array = []
        for i in xrange(len(str_base)):
            temp_char = str_base[i]
            temp_ascii = ord(temp_char)
            result = temp_ascii - 87 if temp_ascii > 57 else temp_ascii - 48
            temp_array.append(result)
        decode_res = temp_array[0] * 36 + temp_array[1]
        return decode_res

    def _decode_response(self, challenge, userresponse):
        if len(userresponse) > 100:
            return 0
        shuzi = (1, 2, 5, 10, 50)
        chongfu = set()
        key = {}
        count = 0
        for i in challenge:
            if i in chongfu:
                continue
            else:
                value = shuzi[count % 5]
                chongfu.add(i)
                count += 1
                key.update({i: value})
        res = 0
        for i in userresponse:
            res += key.get(i, 0)
        res = res - self._decode_rand_base(challenge)
        return res
utils/geetest.py
from django.conf import settings
from api.utils.geetest import GeeTestLib


def verify(verify_data, uid=None, extend_params=None):
    """第三方滑动验证码校验.

    选用第三方的验证组件, 根据参数进行校验
    根据布尔值辨别是否校验通过

    Parameters
    ----------

    verify_data : dict
        请求数据

    uid: string, default: None
        用户UID, 如果存在就免受滑动验证码的限制

    extend_params : dict
        预留的扩展参数

    Returns
    -------
    True OR False
    """

    captcha_config = settings.GEE_TEST

    if captcha_config.get("verify_status"):

        status = True

        if uid in captcha_config.get("not_verify"):
            return True

        gt = GeeTestLib(captcha_config["gee_test_access_id"], captcha_config["gee_test_access_key"])
        challenge = verify_data.get(gt.FN_CHALLENGE, '')
        validate = verify_data.get(gt.FN_VALIDATE, '')
        seccode = verify_data.get(gt.FN_SECCODE, '')
        # status = request.session.get(gt.GT_STATUS_SESSION_KEY, 1)
        # user_id = request.session.get("user_id")

        if status:
            result = gt.success_validate(challenge, validate, seccode, None)
        else:
            result = gt.failback_validate(challenge, validate, seccode)
        return True if result else False
    else:
        return True
utils/captcha_verify.py
import json

from rest_framework.views import APIView
from api.utils.geetest import GeeTestLib
from rest_framework.response import Response

from django.conf import settings


class CaptchaView(APIView):

    def get(self, request):
        gt = GeeTestLib(settings.GEE_TEST["gee_test_access_id"], settings.GEE_TEST["gee_test_access_key"])
        gt.pre_process()
        # 设置 geetest session, 用于是否启用滑动验证码向 geetest 发起远程验证, 如果取不到的话只是对本地轨迹进行校验
        # self.request.session[gt.GT_STATUS_SESSION_KEY] = status
        # request.session["user_id"] = user_id
        response_str = gt.get_response_str()
        response_str = json.loads(response_str)

        return Response({"error_no": 0, "data": response_str})
views/captcha.py

2.在settings中配置geetest需要的参数:

GEE_TEST = {
    "gee_test_access_id": "37ca5631edd1e882721808d35163b3ad",
    "gee_test_access_key": "7eb11ccf3e0953bdd060ed8b60b0c4f5",
    "verify_status": True,  # 是否启用滑动验证码验证组件(True表示启用)
    "not_verify": [
        "2ba6b08d53a4fd27057a32537e2d55ae",
    ],  # 不用验证的用户(存放着用户的uid)
}
settings

3.在urls.py中配置前端要请求的极验路径:

from django.contrib import admin
from django.urls import path
from api.views.login import LoginView
from api.views.captcha import CaptchaView


urlpatterns = [
    path('admin/', admin.site.urls),

    path('login/', LoginView.as_view()),
    # 极验geetest
    path("api/captcha_check/", CaptchaView.as_view()),

]
urls.py

4.在登录视图中调用  utils/captcha_verify.py  中的 verify,通过判断  verify(request.data) 是否为True 来决定用户能否登录成功,验证错误返回错误信息:

from django.contrib import auth
import uuid
import datetime

from rest_framework.views import APIView
from rest_framework.response import Response

from api.models import Token
from api.utils.captcha_verify import verify


class LoginView(APIView):

    def post(self, request):

        res = {'user': None, 'msg': None}
        is_valid = verify(request.data)

        try:
            if is_valid:
                user = request.data.get('user')
                pwd = request.data.get('pwd')
                user_obj = auth.authenticate(username=user, password=pwd)

                if user_obj:
                    random_str = str(uuid.uuid4())
                    Token.objects.update_or_create(user=user_obj, defaults={"key": random_str, 'created': datetime.datetime.now()})
                    res['user'] = user_obj.username
                    res['token'] = random_str
                else:
                    res['msg'] = 'user or pwd error'
            else:
                res['msg'] = '验证码异常!'
        except Exception as e:
            res['msg'] = str(e)

        return Response(res)
views/login.py

 

 

前端 vue 使用(配合后端,主要从后端geetest得到3个重要参数:challenge,validate,seccode)极验方式:

1.先下载gt.js并放在global下:

"v0.4.8 Geetest Inc.";

(function (window) {
    "use strict";
    if (typeof window === 'undefined') {
        throw new Error('Geetest requires browser environment');
    }

var document = window.document;
var Math = window.Math;
var head = document.getElementsByTagName("head")[0];

function _Object(obj) {
    this._obj = obj;
}

_Object.prototype = {
    _each: function (process) {
        var _obj = this._obj;
        for (var k in _obj) {
            if (_obj.hasOwnProperty(k)) {
                process(k, _obj[k]);
            }
        }
        return this;
    }
};

function Config(config) {
    var self = this;
    new _Object(config)._each(function (key, value) {
        self[key] = value;
    });
}

Config.prototype = {
    api_server: 'api.geetest.com',
    protocol: 'http://',
    typePath: '/gettype.php',
    fallback_config: {
        slide: {
            static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
            type: 'slide',
            slide: '/static/js/geetest.0.0.0.js'
        },
        fullpage: {
            static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
            type: 'fullpage',
            fullpage: '/static/js/fullpage.0.0.0.js'
        }
    },
    _get_fallback_config: function () {
        var self = this;
        if (isString(self.type)) {
            return self.fallback_config[self.type];
        } else if (self.new_captcha) {
            return self.fallback_config.fullpage;
        } else {
            return self.fallback_config.slide;
        }
    },
    _extend: function (obj) {
        var self = this;
        new _Object(obj)._each(function (key, value) {
            self[key] = value;
        })
    }
};
var isNumber = function (value) {
    return (typeof value === 'number');
};
var isString = function (value) {
    return (typeof value === 'string');
};
var isBoolean = function (value) {
    return (typeof value === 'boolean');
};
var isObject = function (value) {
    return (typeof value === 'object' && value !== null);
};
var isFunction = function (value) {
    return (typeof value === 'function');
};
var MOBILE = /Mobi/i.test(navigator.userAgent);
var pt = MOBILE ? 3 : 0;

var callbacks = {};
var status = {};

var nowDate = function () {
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var hours = date.getHours();
    var minutes = date.getMinutes();
    var seconds = date.getSeconds();

    if (month >= 1 && month <= 9) {
      month = '0' + month;
    }
    if (day >= 0 && day <= 9) {
      day = '0' + day;
    }
    if (hours >= 0 && hours <= 9) {
      hours = '0' + hours;
    }
    if (minutes >= 0 && minutes <= 9) {
      minutes = '0' + minutes;
    }
    if (seconds >= 0 && seconds <= 9) {
      seconds = '0' + seconds;
    }
    var currentdate = year + '-' + month + '-' + day + " " + hours + ":" + minutes + ":" + seconds;
    return currentdate;
}

var random = function () {
    return parseInt(Math.random() * 10000) + (new Date()).valueOf();
};

var loadScript = function (url, cb) {
    var script = document.createElement("script");
    script.charset = "UTF-8";
    script.async = true;

    // 对geetest的静态资源添加 crossOrigin
    if ( /static\.geetest\.com/g.test(url)) {
        script.crossOrigin = "anonymous";
    }

    script.onerror = function () {
        cb(true);
    };
    var loaded = false;
    script.onload = script.onreadystatechange = function () {
        if (!loaded &&
            (!script.readyState ||
            "loaded" === script.readyState ||
            "complete" === script.readyState)) {

            loaded = true;
            setTimeout(function () {
                cb(false);
            }, 0);
        }
    };
    script.src = url;
    head.appendChild(script);
};

var normalizeDomain = function (domain) {
    // special domain: uems.sysu.edu.cn/jwxt/geetest/
    // return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
    return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
};
var normalizePath = function (path) {
    path = path.replace(/\/+/g, '/');
    if (path.indexOf('/') !== 0) {
        path = '/' + path;
    }
    return path;
};
var normalizeQuery = function (query) {
    if (!query) {
        return '';
    }
    var q = '?';
    new _Object(query)._each(function (key, value) {
        if (isString(value) || isNumber(value) || isBoolean(value)) {
            q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
        }
    });
    if (q === '?') {
        q = '';
    }
    return q.replace(/&$/, '');
};
var makeURL = function (protocol, domain, path, query) {
    domain = normalizeDomain(domain);

    var url = normalizePath(path) + normalizeQuery(query);
    if (domain) {
        url = protocol + domain + url;
    }

    return url;
};

var load = function (config, send, protocol, domains, path, query, cb) {
    var tryRequest = function (at) {

        var url = makeURL(protocol, domains[at], path, query);
        loadScript(url, function (err) {
            if (err) {
                if (at >= domains.length - 1) {
                    cb(true);
                    // report gettype error
                    if (send) {
                        config.error_code = 508;
                        var url = protocol + domains[at] + path;
                        reportError(config, url);
                    }
                } else {
                    tryRequest(at + 1);
                }
            } else {
                cb(false);
            }
        });
    };
    tryRequest(0);
};


var jsonp = function (domains, path, config, callback) {
    if (isObject(config.getLib)) {
        config._extend(config.getLib);
        callback(config);
        return;
    }
    if (config.offline) {
        callback(config._get_fallback_config());
        return;
    }

    var cb = "geetest_" + random();
    window[cb] = function (data) {
        if (data.status == 'success') {
            callback(data.data);
        } else if (!data.status) {
            callback(data);
        } else {
            callback(config._get_fallback_config());
        }
        window[cb] = undefined;
        try {
            delete window[cb];
        } catch (e) {
        }
    };
    load(config, true, config.protocol, domains, path, {
        gt: config.gt,
        callback: cb
    }, function (err) {
        if (err) {
            callback(config._get_fallback_config());
        }
    });
};

var reportError = function (config, url) {
    load(config, false, config.protocol, ['monitor.geetest.com'], '/monitor/send', {
        time: nowDate(),
        captcha_id: config.gt,
        challenge: config.challenge,
        pt: pt,
        exception_url: url,
        error_code: config.error_code
    }, function (err) {})
}

var throwError = function (errorType, config) {
    var errors = {
        networkError: '网络错误',
        gtTypeError: 'gt字段不是字符串类型'
    };
    if (typeof config.onError === 'function') {
        config.onError(errors[errorType]);
    } else {
        throw new Error(errors[errorType]);
    }
};

var detect = function () {
    return window.Geetest || document.getElementById("gt_lib");
};

if (detect()) {
    status.slide = "loaded";
}

window.initGeetest = function (userConfig, callback) {

    var config = new Config(userConfig);

    if (userConfig.https) {
        config.protocol = 'https://';
    } else if (!userConfig.protocol) {
        config.protocol = window.location.protocol + '//';
    }

    // for KFC
    if (userConfig.gt === '050cffef4ae57b5d5e529fea9540b0d1' ||
        userConfig.gt === '3bd38408ae4af923ed36e13819b14d42') {
        config.apiserver = 'yumchina.geetest.com/'; // for old js
        config.api_server = 'yumchina.geetest.com';
    }

    if(userConfig.gt){
        window.GeeGT = userConfig.gt
    }

    if(userConfig.challenge){
        window.GeeChallenge = userConfig.challenge
    }

    if (isObject(userConfig.getType)) {
        config._extend(userConfig.getType);
    }
    jsonp([config.api_server || config.apiserver], config.typePath, config, function (newConfig) {
        var type = newConfig.type;
        var init = function () {
            config._extend(newConfig);
            callback(new window.Geetest(config));
        };

        callbacks[type] = callbacks[type] || [];
        var s = status[type] || 'init';
        if (s === 'init') {
            status[type] = 'loading';

            callbacks[type].push(init);

            load(config, true, config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
                if (err) {
                    status[type] = 'fail';
                    throwError('networkError', config);
                } else {
                    status[type] = 'loaded';
                    var cbs = callbacks[type];
                    for (var i = 0, len = cbs.length; i < len; i = i + 1) {
                        var cb = cbs[i];
                        if (isFunction(cb)) {
                            cb();
                        }
                    }
                    callbacks[type] = [];
                }
            });
        } else if (s === "loaded") {
            init();
        } else if (s === "fail") {
            throwError('networkError', config);
        } else if (s === "loading") {
            callbacks[type].push(init);
        }
    });

};


})(window);
static/global/gt.js

2.在main.js中引入全局的gt.js:

// 引入全局的geetest.js
import '../static/global/gt'
import Vue from 'vue'
import App from './App'
import router from './router'

//1.导入element-ui模块 并且导入全局的css样式
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

//将lfHeader 注册成全局组件
import LfHeader from '@/components/Common/LfHeader'
Vue.component(LfHeader.name,LfHeader);

// 引入vue-cookies
import VueCookies from 'vue-cookies'
Vue.use(VueCookies);

// 引入全局的geetest.js
import '../static/global/gt'

//引入项目中的全局的css样式
import '../static/global/index.css'

import * as api from './restful/api'
Vue.prototype.$http = api;

//导入store实例
import store from './store'

//全局导航守卫
router.beforeEach((to, from, next) => {
  if(VueCookies.isKey('access_token')){
     let user = {
        username:VueCookies.get('username'),
        shop_cart_num:VueCookies.get('shop_cart_num'),
        access_token:VueCookies.get('access_token'),
        avatar:VueCookies.get('avatar'),
        notice_num:VueCookies.get('notice_num')
     };
     store.dispatch('getUser',user)
  }
  next()
});

Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
});
main.js

3.在Login.vue组件中的 created 中写上  this.getGeetest();  ,让登录组件创建完成时就向服务端发送axios请求得到初始化验证界面(captchaObj.appendTo("#geetest")将验证组件放在页面中id="geetest"的标签中,如<div id="geetest"></div>)和3个重要参数(用于二次验证),然后用户点击登录按钮时触发 loginHandler 事件,完成二次验证(注:axios请求都放在 src/restful/api.js 中)

<template>
  <div class="box">
    <img src="https://www.luffycity.com/static/img/Loginbg.3377d0c.jpg" alt>
    <div class="login">
      <div class="login-title">
        <img src="https://www.luffycity.com/static/img/Logotitle.1ba5466.png" alt>
        <p>帮助有志向的年轻人通过努力学习获得体面的工作和生活!</p>
      </div>
      <div class="login_box">
        <div class="title">
          <span>密码登录</span>
          <span>短信登录</span>
        </div>
        <div class="inp">
          <input v-model="username" type="text" placeholder="用户名 / 手机号码" class="user">
          <input v-model="password" type="password" name class="pwd" placeholder="密码">
          <div id="geetest"></div>
          <div class="rember">
            <p>
              <input type="checkbox" class="no" name="a">
              <span>记住密码</span>
            </p>
            <p>忘记密码</p>
          </div>
          <button class="login_btn" @click="loginHandler">登录</button>
          <p class="go_login">
            没有账号
            <span>立即注册</span>
          </p>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "Login",
  data() {
    return {
      username: "",
      password: "",
      geetestObj:{}
    };
  },
  methods: {
    loginHandler(){
        if(!this.geetestObj){
            return;
        }else{
            //登录状态
            let params = {
                geetest_challenge: this.geetestObj.geetest_challenge,
                geetest_seccode: this.geetestObj.geetest_seccode,
                geetest_validate: this.geetestObj.geetest_validate,
                username:this.username,
                password:this.password
            }
            this.$http.login(params)
            .then(res=>{
                if(res.error_no === 0){
                    //前面的记录
                    this.$router.go(-1)
                    let data = res.data;
                    //存储cookie  通过for-in循环来遍历 字典对象
                    for(let key in data){
                        this.$cookies.set(key,data[key])
                    }
                    //分发actions中声明的方法
                    this.$store.dispatch('getUser',data);
                }
            })
            .catch(err=>{
                console.log(err)
            })
        }
    },
    //
    getGeetest() {
      this.$http
        .geetest()
        .then(res => {
          let data = res.data;
          initGeetest(
            {
              // 以下配置参数来自服务端 SDK
              gt: data.gt,
              challenge: data.challenge,
              offline: !data.success,
              new_captcha: true,
              width: "100%"
            },
            (captchaObj)=> {
              // 这里可以调用验证实例 captchaObj 的实例方法
              captchaObj.appendTo("#geetest");

              // 这里调用了 onSuccess 方法
              captchaObj.onSuccess(()=> {
                var result = captchaObj.getValidate();
                this.geetestObj = result
              });
            }
          );
        })
        .catch(err => {
          console.log(err);
        });
    }
  },
  created() {
    this.getGeetest();
  }
};
</script>

<style lang="css" scoped>
.box {
  width: 100%;
  position: relative;
}
.box img {
  width: 100%;
}
.box .login {
  position: absolute;
  width: 500px;
  height: 400px;
  top: 50%;
  left: 50%;
  margin-left: -250px;
  margin-top: -300px;
}
.login .login-title {
  width: 100%;
  text-align: center;
}
.login-title img {
  width: 190px;
  height: auto;
}
.login-title p {
  font-family: PingFangSC-Regular;
  font-size: 18px;
  color: #fff;
  letter-spacing: 0.29px;
  padding-top: 10px;
  padding-bottom: 50px;
}
.login_box {
  width: 400px;
  height: auto;
  background: #fff;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  margin: 0 auto;
  padding-bottom: 40px;
}
.login_box .title {
  font-size: 20px;
  color: #9b9b9b;
  letter-spacing: 0.32px;
  border-bottom: 1px solid #e6e6e6;
  display: flex;
  justify-content: space-around;
  padding: 50px 60px 0 60px;
  margin-bottom: 20px;
  cursor: pointer;
}
.login_box .title span:nth-of-type(1) {
  color: #4a4a4a;
  border-bottom: 2px solid #84cc39;
}

.inp {
  width: 350px;
  margin: 0 auto;
}
.inp input {
  border: 0;
  outline: 0;
  width: 100%;
  height: 45px;
  border-radius: 4px;
  border: 1px solid #d9d9d9;
  text-indent: 20px;
  font-size: 14px;
  background: #fff !important;
}
.inp input.user {
  margin-bottom: 16px;
}
.inp .rember {
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: relative;
  margin-top: 10px;
}
.inp .rember p:first-of-type {
  font-size: 12px;
  color: #4a4a4a;
  letter-spacing: 0.19px;
  margin-left: 22px;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-align: center;
  align-items: center;
  /*position: relative;*/
}
.inp .rember p:nth-of-type(2) {
  font-size: 14px;
  color: #9b9b9b;
  letter-spacing: 0.19px;
  cursor: pointer;
}

.inp .rember input {
  outline: 0;
  width: 15px;
  height: 45px;
  border-radius: 4px;
  border: 1px solid #d9d9d9;
  text-indent: 20px;
  font-size: 14px;
  background: #fff !important;
}

.inp .rember p span {
  display: inline-block;
  font-size: 12px;
  width: 100px;
  /*position: absolute;*/
  /*left: 20px;*/
}
#geetest {
  margin-top: 20px;
}
.login_btn {
  width: 100%;
  height: 45px;
  background: #84cc39;
  border-radius: 5px;
  font-size: 16px;
  color: #fff;
  letter-spacing: 0.26px;
  margin-top: 30px;
}
.inp .go_login {
  text-align: center;
  font-size: 14px;
  color: #9b9b9b;
  letter-spacing: 0.26px;
  padding-top: 20px;
}
.inp .go_login span {
  color: #84cc39;
  cursor: pointer;
}
</style>
src/components/Login/Login.vue
import Axios from 'axios'
import VueCookies from 'vue-cookies'
Axios.defaults.baseURL='http://127.0.0.1:8000/';
const captchaCheckUrl = 'api/captcha_check/';
const loginUrl = 'login/';

//请求拦截器
Axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    if(VueCookies.isKey('access_token')){
        //有问题?
       config.headers.Authorization = VueCookies.get('access_token');
    }
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });
export function geetest(){
    return Axios.get(`${captchaCheckUrl}`).then(res=>res.data)
}
export function login(params){
    return Axios.post(`${loginUrl}`,params).then(res=>res.data)
}
src/restful/api.js

posted on 2019-11-13 19:54  始终不够啊  阅读(589)  评论(0编辑  收藏  举报