Thanks to visit codestin.com
Credit goes to gitcode.com

appauth4cj:基于 OAuth 2.0 和 OpenID Connect 的身份认证库项目

appauth4cj 库用于安全的用户登录和访问受保护资源,遵循 OAuth 2.0 和 OpenID Connect 业界标准协议。

分支5Tags3

appauth4cj

介绍

appauth4cj 库,帮助应用安全地实现用户登录和访问受保护资源的功能。 它遵循 OAuth 2.0 和 OpenID Connect 这两个业界标准协议。

效果展示

特性

  • 🚀 帮助用户通过第三方服务登录你的应用
  • 🚀 获取访问用户数据的权限,比如获取用户的邮箱、头像等信息
  • 🚀 支持兼容 OAuth 2.0/OpenID Connect 的服务
  • 🚀 提供授权信息的序列化和反序列化,方便存储

软件架构

OAuth 2.0

img_1.png

注:图片来自华为账号服务OAuth 2.0鉴权说明文档

具体步骤

  1. 用户点击登录按钮:用户在你的应用中点击"使用 华为账号 登录"

  2. 打开浏览器进行认证:AppAuth 自动打开 华为账号 的登录页面

  3. 用户输入凭据:用户在页面输入用户名和密码

  4. 获取授权码:登录成功后,华为账号服务器 返回一个临时的授权码给你的应用

  5. 换取访问令牌:AppAuth 使用授权码向 华为账号服务器 换取访问令牌

  6. 访问用户信息:使用访问令牌获取用户信息(如姓名、邮箱等)

  7. 自动刷新令牌:当令牌过期时,自动刷新获取新令牌

OpenID Connect

OpenID Connect 在OAuth2.0协议上完善了身份认证

只使用 OAuth2.0 时的请求和响应结构结构,授权服务器正常返回令牌

请求

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=xxxxxxxxxxx&
client_id=your_client_id&
client_secret=your_client_secret_only_known_by_server&
redirect_uri=https%3A//oauth2.example.com/code

响应

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
"access_token": "dasdqwdqd",
"token_type": "Bearer",
"refresh_token": "casdqwfw",
"expires_in": 3600
}

增加OpenID Connect后,请求和响应都有改变,授权服务器会多返回ID令牌

请求

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
response_type=code&
scope=openid%20profile%20email // 增加openid
client_id=your_client_id&
client_secret=your_client_secret_only_known_by_server&
redirect_uri=https%3A//oauth2.example.com/code

响应

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
"access_token": "dasdqwdqd",
"token_type": "Bearer",
"refresh_token": "casdqwfw",
"expires_in": 3600,
"id_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // id令牌是jwt结构的编码令牌,可以解码出如邮箱、用户标识、令牌有效时间等信息
}

源码目录

/appauth4cj                  # 项目根目录
├── doc                      # 文档目录
│   ├── assets               # 文档资源目录
│   └── feature_api.md       # API接口文档
├── entry                    # 示例代码文件夹
├── appauth                  # appauth库文件夹
│    └─ src/main/cangjie     # 核心代码                  
├── README.md                # 安装使用方法      

接口说明

主要类和函数接口说明详见 API

编译构建

stdx的配置:

  1. 下载 stdx 并解压到 ${DEVECO_OH_NATIVE_HOME} 目录下

    • 如果是单独安装的cangjie插件,那么 ${DEVECO_OH_NATIVE_HOME} 目录一般在 C:\Users\你的用户名.cangjie-sdk\5.1\cangjie
    • 如果是一体化的版本,那么 ${DEVECO_OH_NATIVE_HOME} 目录一般在 你的DevEco安装目录\plugins\cangjie\sdk\cangjie
  2. 在 entry 和 appauth 文件夹下的 cjpm.toml 文件中添加stdx的配置 初始cjpm.toml文件

[target]
    [target.aarch64-linux-ohos]
        compile-option = "......"
        [target.aarch64-linux-ohos.bin-dependencies]
            path-option = [ "...", "...", "..."]
            [target.aarch64-linux-ohos.bin-dependencies.package-option]
   [target.x86_64-linux-ohos]
        compile-option = "......"
        [target.x86_64-linux-ohos.bin-dependencies]
            path-option = [ "...", "...", "..."]
   [target.x86_64-unknown-windows-gnu]
        [target.x86_64-unknown-windows-gnu.bin-dependencies]
            path-option = [ "...", "..." ]
        [target.x86_64-unknown-windows-gnu.bin-dependencies.package-option]

分别在每个 xxx.bin-dependencies 的 path-option 里面添加 stdx 的路径,注意对应不同的文件夹名称

[target]
    [target.aarch64-linux-ohos]
        compile-option = "......"
        [target.aarch64-linux-ohos.bin-dependencies]
            path-option = [ "...", "...", "...", "${DEVECO_CANGJIE_HOME}/linux_ohos_aarch64_llvm/dynamic/stdx" ]
            [target.aarch64-linux-ohos.bin-dependencies.package-option]
   [target.x86_64-linux-ohos]
        compile-option = "......"
        [target.x86_64-linux-ohos.bin-dependencies]
            path-option = [ "...", "...", "...", "${DEVECO_CANGJIE_HOME}/linux_ohos_x86_64_llvm/dynamic/stdx" ]
   [target.x86_64-unknown-windows-gnu]
        [target.x86_64-unknown-windows-gnu.bin-dependencies]
            path-option = [ "...", "...", "${DEVECO_CANGJIE_HOME}/windows_x86_64_llvm/dynamic/stdx" ]
        [target.x86_64-unknown-windows-gnu.bin-dependencies.package-option]

1.module方式引入

  1. 克隆下载项目
  2. 将 appauth 模块拷贝到应用项目下
  3. 修改自身应用 entry 下的 oh-package.json5 文件,在 dependencies 字段添加 "appauth": "file:../appauth"
{
  "name": "entry",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {
    "appauth": "file:../appauth"
  }
}
  1. 同步应用 entry/ 下的 cjpm.toml 文件,会自动添加依赖,如果没有可以手动在 [dependencies] 字段下添加 appauth = {path = "../../../../appauth", version = "1.0.0"},这里需要配置自己机器上的stdx,具体配置步骤见stdx说明。
[dependencies]
appauth = {path = "../../../../appauth", version = "1.0.0"}
  1. 在项目中引用
  import appauth.*

2.三方库方式引入

  1. 目标工程把 appauth4cj 依赖库作为 git submodule 引入
> cd $工程根目录
> mkdir third-party
> cd third-party
> git submodule add "https://gitcode.com/Cangjie-TPC/appauth4cj.git"
  1. 修改自身应用 entry/ 下的 cjpm.toml 文件,添加依赖
[dependencies]
appauth = {path = "../../../../third-party/appauth4cj/appauth", version = "1.0.0"}
  1. 在项目中引用
import appauth.*

使用说明

功能示例

@Entry
@Component
public class MainRegistration {
    // -------- 授权平台的相关信息
    // 在授权平台上注册的应用的id
    var cliendId: String = ""
    // 授权平台生成的应用的密钥
    var clientSecret = ""
    // 授权信息回调地址(一般在授权平台上自定义填写)
    var redirectUri: String = ""
    // 授权范围(不同授权平台的范围可能有不同,参考授权平台的相关文档)
    var authorizationScope: String = "openid email"
    // 授权请求地址
    var authorizationEndpointUri: String = ""
    // 令牌请求地址
    var tokenEndpointUri: String = ""
    // discovery文档地址(discovery文档一般会保存所有的授权相关的信息)
    var discoveryUri: String = ""
    // 授权模式,code表示授权码模式
    var responseType: String = "code"
    // 用户信息请求地址---非必须,有的平台可能不提供
    var userInfoEndpointUri: String = ""
    // 动态注册请求地址---非必须,有的平台可能不提供
    var registrationEndpointUri: String = ""
    // 令牌注销请求地址---非必须,有的平台可能不提供
    var endSessionEndpoint: String = ""

    public let TAG: String = "MainRegistration"
    let KEY_STATE: String = "state"
    // 上下文对象
    let stageContext = Global.getCurrentStageContext()
    // Preferences轻量级数据库,用于本地持久化保存
    let preferences = Preferences.getPreferences(stageContext, "myPreferences")
    // Webview控制器
    var webController: WebviewController = WebviewController()

    @State // Webview初始加载地址
    var currentUrl: String = "https://www.test.com"
    // 授权信息记录对象,用于持久化保存
    var authState: ?AuthState = Option<AuthState>.None
    // 授权请求服务
    var service: AuthorizationService = AuthorizationService()
    var authorizationRequestString: String = ""
    // 本地保存时需要用到的锁
    let mPrefsLock: ReentrantMutex = ReentrantMutex()
    
    func build() {
        Column {
            Button("webView加载授权请求界面")
                .fontSize(14).width(100.percent).margin(5)
                .onClick {
                    =>
                        Hilog.error(666, TAG, "------------> webView加载授权请求界面")
                        var request = createAuthRequest()
                        authorizationRequestString = request.jsonSerializeString()
                        webController.loadUrl(request.toUri().toString())
                }
            
            Web(src: currentUrl, controller: webController)
                .width(100.percent)
                .onPageBegin({event: OnPageEvent =>
                    Hilog.error(666, TAG, "------------> onPageBegin")
                })
                .onPageEnd({event: OnPageEvent =>
                    Hilog.error(666, TAG, "------------> onPageEnd: ${event.url}")
                    // webView上登录/授权后,接收到的返回信息,是授权平台填写的回调地址和code授权码、state状态标记
                    getToken(event.url)
                })
                .onLoadIntercept({event: WebResourceRequest =>
                    Hilog.error(666, TAG, "------------> onLoadIntercept")
                    return false
                })
        }
    }
    
    // 构建授权请求对象
    public func createAuthRequest(): AuthorizationRequest {
    	var builderAuthorization = AuthUri.parse(authorizationEndpointUri).buildUpon()
        var builderToken = AuthUri.parse(tokenEndpointUri).buildUpon()

        var configuration = AuthorizationServiceConfiguration(builderAuthorization.build(), builderToken.build())
        var redirectUriTmp = AuthUri.parse(redirectUri)
        var builder = AuthorizationRequest_Builder(configuration, cliendId, responseType, redirectUriTmp).setScope(authorizationScope)
        return builder.build()
    }
    
    // 处理授权请求返回,发起token请求
    public func getToken(response: String) {
        // 判断返回值是不是以回调地址开头
        // 是,表示是需要处理的返回值
        // 不是,表示可能是后续重定向之类的,不做处理
        if (!response.startsWith(redirectUri)) {
            Hilog.error(666, TAG, "------------> 拦截非token请求")
        	return
        }
        var responseUri = AuthUri.parse(response).buildUpon().build()
        var errorMsg = responseUri.getQueryParameter("error")
        if (!errorMsg.isEmpty()) {
        	Hilog.error(666, TAG, "------------> 返回值报错:${errorMsg}")
            return
        }

        var authorizationRequest = AuthorizationRequest.jsonDeserialize(authorizationRequestString)
        // 因为是直接用应用接收返回值,需要手动构造AuthorizationResponse对象
        var authorizationResponse = AuthorizationResponse_Builder(authorizationRequest)
            .setState(responseUri.getQueryParameter("state"))
            .setAuthorizationCode(responseUri.getQueryParameter("code"))
            .build()
        var callBack = { response: ?TokenResponse, ex: ?AuthorizationException =>
            if (ex.isNone() && response.isSome()) {
            	var currentState = getCurrentState()
                currentState.update(response, ex)
                replace(currentState)
                Hilog.error(666, TAG, "------------> 请求令牌成功")
                return true
            } else {
                Hilog.error(666, TAG, "------------> 请求令牌失败:${ex.getOrThrow().message}")
                return false
            }
        }

        var request: TokenRequest = authorizationResponse.createTokenExchangeRequest()
        service.performTokenRequest(request, ClientSecretPost(clientSecret), callBack)
    }
}

约束与限制

在下述版本验证通过:

DevEco Studio 5.1.1 Release (5.1.1.823)
Cangjie Support Plugin 5.1.1.823
Cangjie Compiler: 1.0.1 (cjnative)

开源协议

本项目基于 Apache License 2.0,请自由的享受和参与开源。

参与贡献

欢迎给我们提交PR,欢迎给我们提交Issue,欢迎参与任何形式的贡献。

项目介绍

appauth4cj 库用于安全的用户登录和访问受保护资源,遵循 OAuth 2.0 和 OpenID Connect 业界标准协议。

定制我的领域