QQ互联的注意事项

业务逻辑

最近试了一下用QQ登陆联系原有的账户体系,由于用了LeanCloud提供的后台服务,我只用关心QQ互联的部分。

首先,它的业务逻辑是:你先设置一个按钮,要跳转到QQ登陆页面,登陆完成后跳转到回调页面。回调页面即可以是用户主页也可以是一个中间页面用于跳转。

在PC上,曾经常见的模式是弹出QQ登陆小窗,登陆后原页面收到登陆成功的回调更新登陆状态。

但以上的方式如果在移动端使用,由于没有回调地址,唤起QQ登陆后无法跳转到用户界面,并且原页面也不会收到回调。

所以现在有一个模式,就是点击QQ登陆后原页面直接跳转到QQ登陆页面,登陆成功后回调地址收到accessToken与openId,之后再处理业务逻辑。

OAuth 2.0

A->B->C->D->E
先获取Code,然后再获得Token,重定向URI必须保持一致

现在你可以用他们的API来实现以上功能,比如QQ互联提供了QQ登陆地址的格式,通过Authorization获取AccessToken,通过AccessToken获取openId的接口,以及getUserInfo等api的接口。但我尝试后发现这些api没有设置跨域头,如果直接在前端获取会被浏览器拦截,因此我选择用他们的PHP SDK。

PHP SDK

SDK使用虽然很方便,但还是没有设置跨域头,你需要在页面输出前设置Access-Allow-Control-Origin。还没完,PHP SDK使用了session,不能像官方的获取用户信息的api一样直接请求,你还需要设置Access-Alllow-Control-Credentials,在前端请求时如果用AXIOS,也要加上withCredentials的配置。

由于我习惯把不涉及安全业务逻辑放到前端,所以我在callback.php里将AccessToken与OpenId传给前端的Login组件,由前端请求后判断是否是新用户等等。(也由于使用了LeanCloud,php并不能直接查数据库,所以干脆放在前端)

JS SDK

QQ互联我花了不少时间,主要是理解它的业务逻辑,还有跨域的问题。我开始用的是JS SDK,但似乎它已年久失修(12年更新过),而且它要求用script标签引入,我在前端还要检查SDK对象是否已加载完成,让我的代码很混乱,加上还是要后端协助解决跨域,所以还是不用了。

PHP&CURL 小插曲

其实还有一个小插曲,PHP SDK需要curl,但我安装的php 7.3 似乎并编译不上,而且php7.2-curl都是有的,但php7.3-curl还没有。。。只好换成7.2。

npm,yarn如何查看源和换源

转自https://zhuanlan.zhihu.com/p/35856841

重新安装npm后总要设置一下

npm, yarn查看源和换源:

npm config get registry  // 查看npm当前镜像源

npm config set registry https://registry.npm.taobao.org/  // 设置npm镜像源为淘宝镜像

yarn config get registry  // 查看yarn当前镜像源

yarn config set registry https://registry.npm.taobao.org/  // 设置yarn镜像源为淘宝镜像

镜像源地址部分如下:

npm --- https://registry.npmjs.org/

cnpm --- https://r.cnpmjs.org/

taobao --- https://registry.npm.taobao.org/

nj --- https://registry.nodejitsu.com/

rednpm --- https://registry.mirror.cqupt.edu.cn/

npmMirror --- https://skimdb.npmjs.com/registry/

deunpm --- http://registry.enpmjs.org/

Json Web Tokens

这篇文章是对网络上相关信息的总结,详情可以参考《JSON Web Token 入门教程》

http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

使用JWT

JWT作为一道具有过期时间的令牌存储在Cookie或localStorage,与服务器通信时都会捎带上。既可以写在Cookie里,也可以放在HTTP头中,还可以写在具体的请求里。

JWT结构

JWT由3个JSON对象组成,最终的JWT是经过转码(参考Base64)的字符串。

Header

键包括alg(algorithm)与typ(type),指出了签名算法与令牌类型。

Payload

JWT传输的信息,键包括生效时间、过期时间、签发人等。

Signature

在服务器上用密钥生成Header与Payload的签名,防止JWT信息被篡改。

JWT注意事项

  • JWT默认不加密,有需要可以加密
  • 如果不加密,建议使用HTTPS协议防止被盗用
  • JWT有效时长无法更改,不建议设置时间太长

用CSS画一个QQ音乐图标

主要用到了relative定位、border的垂直和水平分量,之所以用区块遮盖实现内凹,因为radial-gradient我不太熟悉。

<head>
    <style>
         :root {
            --color: rgb(68, 202, 68);
        }
        
        .div {
            width: 240px;
            height: 240px;
            border-radius: 120px;
            background-color: rgb(235, 227, 128);
        }
        
        .music {
            --vertical: 40px;
            --horizontal: 50px;
            width: 100px;
            height: 80px;
            background: var(--color);
            border-bottom-left-radius: var(--horizontal) var(--vertical);
            border-bottom-right-radius: var(--horizontal) var(--vertical);
            border-top-left-radius: var(--horizontal) var(--vertical);
            border-top-right-radius: var(--horizontal) var(--vertical);
            position: relative;
            top: calc(100% - 90px);
            left: calc((100% - 100px)/2);
        }
        
        .pole {
            height: 170px;
            width: 10px;
            background: var(--color);
            position: relative;
            left: 52px;
            bottom: 120px;
            border: 10px;
            transform: rotate(-27deg);
        }
        
        .flag {
            width: 80px;
            height: 40px;
            background-color: var(--color);
            border-bottom-right-radius: 60px 35px;
        }
        
        .block {
            background: rgb(235, 227, 128);
            width: 80px;
            height: 20px;
            border-bottom-left-radius: 70px 20px;
            position: relative;
            bottom: 1px
        }
    </style>
</head>

<body>
    <div class="div">
        <div class="music">
            <div class="pole">
                <div class="flag">
                    <div class="block"></div>
                </div>
            </div>
        </div>

    </div>
</body>

CSS Functions && CSS Variables

CSS Variables

Syntax

{
--variable:#ffffff;
color: var(--variable);
}

CSS Functions

calc()

calc()用于对数值作计算,单位包括长宽(px、vw等)、频率(Hz等)、角度(deg、rad、turn等)、时间(s、ms)、小数和整数。

calc()要求+和-的左右有空格。

例如,width:calc(100% - 30px)定义了比父元素宽度小30px的宽度。

attr()

attr()用于取回被选择元素的属性,比如对一个<img>标签,可以用attr(src)获取图片地址。如果css选择器选择了多个元素,attr()在每个元素上可能有不同结果。

max()

取最大值。

min()

取最小值。

fit-content()

fit-content()接受一个参数,如果这个参数超过可设置的最大值,那么用最大值替代,如果小于最小值,则用最小值替代。原理其实是min(maximum size, max(minimum size, argument))。

env()

env()的作用类似于var(),但env()除了返回值,还会改变user agent-defined环境变量的值,MDN有如下例子:

body {
  padding:
    env(safe-area-inset-top, 20px)
    env(safe-area-inset-right, 20px)
    env(safe-area-inset-bottom, 20px)
    env(safe-area-inset-left, 20px);
}

第二个参数是可以省略的,这样不会更改user agent-defined环境变量的值。

user agent stylesheet即浏览器默认样式表,比如<h1></h1>会自带margin。有时这会给开发和适配带来不便,解决方案是使用reset.css。

counter()

MDN示例,看完应该明白了:

HTML

<ol>
  <li></li>
  <li></li>
  <li></li>
</ol>

CSS

ol {
  counter-reset: listCounter;/**重置计数器**/
}
li {
  counter-increment: listCounter;/**每多一个li元素listCounter+1**/
}
li::after {
  content: "[" counter(listCounter) "] == [" counter(listCounter, upper-roman) "]";/**通过counter获取listCounter的值**/
}

RESULT

linear-gradient()

线性渐变图像,支持设置颜色、角度、位置(百分比位置),例如:

background: linear-gradient(0.5turn, #e66465 0%, #9198e5 100%);

repeat()、minmax()

和Grid配合使用,详情参考CSS Grid

rotate()、matrix()、scale()、translate()、skew()等

旋转、矩阵表达的线性变换、缩放、移动、扭曲,详情参考CSS transform

opacity()、blur()等

透明、模糊,详情参考CSS filter

一些实验特性

element()

比如background:element(#id)可以以#id选择的元素为背景,element()使元素作为图片使用。仅Firefox支持。

JavaScript原型链档案

原型链是JavaScript中的“继承”。

JavaScript中的“类”

事实上JavaScript一直以来都是基于对象和原型的,除了Number、String、Boolean等基本数据类型之外,JavaScript中的一切都是对象。ES6中新增的class、constructor、static、extends、super等关键字都是基于对象和原型的语法糖。

JavaScript中的“继承”

我们知道extends关键字可以用来继承类,其实类会被babel编译成函数(也是对象),而对象就有原型链的说法。

什么是原型链

通俗地讲,你创建了一个对象a,然后基于对象a又创建了一个对象b(使用Object.create(obj)),这时访问b的属性,如果b没有这个属性,那么JavaScript会在b的原型中寻找这个属性,而b的原型包含a的所有属性。这样的继承关系可以存在于更多对象间,比如a->b->c从而形成了原型链。

JavaScript中原型链的使用

如何访问原型链

在JavaScript中,每一个对象都有一个__proto__属性,我们可以通过Obj.__proto__访问原型。

构造函数constructor有一个prototype属性,用构造函数创建的对象的__proto__实际上指向了constructor的prototype属性。我们可以通过Object.prototype查看Object的原型对象。

使用Class

Class与extends完全基于原型链。

使用new

用new创建对象时,对象的__proto__会指向构造器的prototype。在JavaScript中,构造器不一定是constructor(),任何一个函数(除箭头函数)都可以成为构造器,因此,只要设置好某个函数的prototype属性,new得到的对象就能使用原型链。

使用Object.create()

该函数的第一个参数就是新对象的原型。

使用JavaScript类型

例如,Array数组的原型链arr -> Array.prototype -> Object.prototype,Function类似。

如果你想实现VueRouter不得不看这篇文章

转载

HTML5 简介(三):利用 History API 无刷新更改地址栏

February 12, 2014 / 编程指南

HTML5 新增的历史记录 API 可以实现无刷新更改地址栏链接,配合 AJAX 可以做到无刷新跳转。

简单来说:假设当前页面为renfei.org/,那么执行下面的 JavaScript 语句:

window.history.pushState(null, null, "/profile/");

之后,地址栏的地址就会变成renfei.org/profile/,但同时浏览器不会刷新页面,甚至不会检测目标页面是否存在。

pushState 方法

上面的语句实际上用到了 HTML5 的历史记录 API。这套 API 提供一种「人为操纵」浏览器历史记录的方法。

(更多…)