Abp.io(vNext)开发日志:单页面应用与外部/社交登录
Posted 上将军
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Abp.io(vNext)开发日志:单页面应用与外部/社交登录相关的知识,希望对你有一定的参考价值。
@TOC
Abp.io(vNext)开发日志:单页面应用与外部/社交登录
如果不使用外部/社交登录,实现很简单,使用Identity Server 4
的令牌端点(Toekn EndPoint
)实现密码
登录获取到令牌就行了。
如果要使用外部/社交登录,因为没有用户名和密码,需要使用令牌端点(Toekn EndPoint
)的授权码(authorization_code)
来登录。因而,如何获取授权码
是整个流程的关键。
获取授权码
的流程
在Identity Server 4
的端点中,提供了一个名未授权端点(Authorize Endpoint)
的端点,而这就是获取授权码
的关键。整个登录流程的执行步骤如下:
- 将应用程序的地址作为返回地址生成一个访问
授权端点
的地址 - 将第1步生成的地址作为返回地址调整到登录页面
- 在用户登录后,会跳转到
授权端点
授权端点
会根据返回地址,将授权码
等信息与查询字符串的形式返回应用程序首页- 应用程序首页在获取查询字符串中的
授权码
后,通过令牌端点(Toekn EndPoint
)的授权码
方式获取访问令牌
在以上5个步骤中,最大的难度在生成授权端点
的地址和拆解授权码,因为这涉及到sha256
加密等许多安全操作,因而,最简捷的方式是使用封装好的库。如果非要全部自己来实现,如果没时间限制,可以慢慢做,如果有时间限制,建议还是使用库,或者根据现有的库实现适用于自己的简版。
Angular的库
基于Angular
的应用程序可以直接使用库angular-oauth2-oidc
,这是有完整功能的库,直接使用就行了。其实,abp已经为你封装好全流程了,不需要做任何修改就能用了。
其他框架的库
如果不是使用Angular
框架,建议使用[oidc-client-js](https://github.com/IdentityModel/oidc-client-js)
库,做点简单的配置就可以实现所需的功能了。
自定义功能
使用oidc-client-js
的唯一缺点就是库太大了,整个压缩包是373K。笔者习惯的框架是Ext JS,打包后的应用程序是2M起步的,不想再加任何大包了。于是决定自定义登录功能。
在项目明确需要使用微信登录后,需要将原有的密码
登录修改为授权码
登录。整个登录流程都需要改变。
首先要修改的登录方法,代码如下:
login()
let me = this;
if(!me.tryLogin())
let url = this.createLoginUrl('', '', null, false, );
window.location.href = url;
,
这里的主要变化是先尝试调用tryLogin
方法来获取授权码
,如果获取不到,说明这不是登录后跳转回来的地址,需要调用createLoginUrl
方法创建登录地址,然后跳转到登录页面。
以下是tryLogin
方法的代码:
tryLogin()
const me = this;
options = AppConfig.oAuthConfig;
const querySource = window.location.search;
const parts = me.getCodePartsFromUrl(querySource);
const code = parts['code'];
const state = parts['state'];
const sessionState = parts['session_state'];
if (!options.preventClearHashAfterLogin)
const href = location.href
.replace(/[&\\?]code=[^&\\$]*/, '')
.replace(/[&\\?]scope=[^&\\$]*/, '')
.replace(/[&\\?]state=[^&\\$]*/, '')
.replace(/[&\\?]session_state=[^&\\$]*/, '');
history.replaceState(null, window.name, href);
let [nonceInState, userState] = me.parseState(state);
me.state = userState;
if (parts['error'])
return false;
if (!nonceInState)
return false;
const success = me.validateNonce(nonceInState);
if (!success)
return false;
AppStorage.set('session_state', sessionState);
if (code)
me.getTokenFromCode(code, options);
return true;
else
return false;
,
代码的主要功能是从查询字符串中获取到code
、state
和sessionState
的值,然后拆解状态值,验证nonce
值是否正确,其实整个过程就是一次验签过程。在验签通过后,就要调用getTokenFromCode
方法来获取访问令牌了。使用授权码
获取令牌,以下参数是必须的
- grant_type:值必须为
authorization_code
- code: 就是在
tryLogin
方法中获取的code
- redirect_uri: 就是本地url地址
redirectUri
,更准确的说是生成授权码
跳转地址时使用的redirectUri
- code_verifier:在生成
授权码
跳转地址时保存到本地存储的验证码(PKCE_verifier)
- client_id:客户端标识
- client_secret:客户端密钥
以下是createLoginUrl
的代码:
createLoginUrl()
let me = this,
redirectUri = me.redirectUri,
nonce = me.createAndSaveNonce(),
state = nonce,
seperationChar = me.loginUrl.indexOf('?') > -1 ? '&' : '?';
let scope = AppConfig.oAuthConfig.scope;
if (me.oidc && !scope.match(/(^|\\s)openid($|\\s)/))
scope = 'openid ' + scope;
let url = me.loginUrl +
seperationChar +
'response_type=' +
encodeURIComponent(me.responseType) +
'&client_id=' +
encodeURIComponent(me.clientId) +
'&state=' +
encodeURIComponent(state) +
'&redirect_uri=' +
encodeURIComponent(redirectUri) +
'&scope=' +
encodeURIComponent(scope);
if (me.responseType.includes('code') && !me.disablePKCE)
const [challenge, verifier] = me.createChallangeVerifierPairForPKCE();
AppStorage.set('PKCE_verifier', verifier);
url += '&code_challenge=' + challenge;
url += '&code_challenge_method=S256';
if (me.resource)
url += '&resource=' + encodeURIComponent(me.resource);
if (me.oidc)
url += '&nonce=' + encodeURIComponent(nonce);
return url;
,
整个createLoginUrl
方法的主要功能就是构建一个访问地址,其中比较重要的步骤是调用createAndSaveNonce
方法创建随机数,以及调用createChallangeVerifierPairForPKCE
方法创建一个类似于签名的验证码。这些方法都可以从上面提到的两个库中抄,这里就不赘述了。
在调用createChallangeVerifierPairForPKCE
方法时,需要对数据进行SHA256
加密操作,需要一个加密库,建议使用Angular
库的那个,库文件才9k,非常实用。
以上是关于Abp.io(vNext)开发日志:单页面应用与外部/社交登录的主要内容,如果未能解决你的问题,请参考以下文章