oauth2.0个人开发心得

Oauth2.0协议

起初接到项目的时候自己还不了解什么是Oauth2.0协议,项目大概的意思是要做一个对外开放的数据查询服务。由于我们是做医疗行业的公司,使用我们的系统的用户大都是固定的医院和机构,所以用户扩展速度非常慢,可以不做额外的注册功能,使用现有的登陆功能即可完成身份验证。

Oauth2.0协议对于我们来说的应用场景

简单来说。就是用户正在使用第三方的平台,这时需要一些我们平台的数据,这个用户在第三方平台触发登陆授权,我们平台就允许第三方平台主动发起对应的数据查询请求,并返回结果。

Oauth2.0协议原理

原理网上真的是非常多,随便查一下就有了。我大致说下我的理解吧。

第三方网站 授权服务器 数据服务器 发起授权请求(携带用户信息) 分配授权码(code) 使用授权码请求accessToken 分配accessToken(以及refreshToken) 使用accessToken获取数据 返回数据 第三方网站 授权服务器 数据服务器

我找了个介绍的文章,可以自行查看 https://www.cnblogs.com/Wddpct/p/8976480.html

调研过程

Oauth2.0使用方法 QQ与微博的研究

QQ开放平台文档:http://wiki.open.qq.com/wiki/website/开发攻略_Server-side
微博开放平台文档:https://open.weibo.com/authentication

调研过程中,主要选择了QQ和微博的授权登陆来看。

QQ

首先 QQ的登陆授权感觉要比别的复杂一点,复杂来源于QQ可以自动识别当前已经登陆的用户。作为前端小白,不明白其中的道理,所以感觉处理逻辑应该不简单。
用户在第三方网站点击QQ登陆授权按钮的时候,会跳转到一个授权网页。如果登陆了QQ的话就可以点击用户头像直接登陆,如果没有的话就要输入用户名密码来登陆。登陆后点击授权即可。
我感觉,这个授权就是页面来做登陆,登陆后拿到用户信息,然后给授权服务器传用户id之类的参数,授权服务器直接根据用户ID来绑定授权码。

weibo

微博的登陆页面个人感觉相对简单,可能是因为微博客户端没有qq那么普遍吧,它的登陆页面没有识别是否微博登陆,直接出现用户名密码的输入框,然后点击授权。根据用户名密码获取用户信息的操作都放在点击授权之后做了。
应该是授权服务器根据用户名密码去获取了一次用户信息,如果没问题就返回授权码。

实现逻辑

知道了大概的原理,后边实现起来就没什么难度了。
使用 mysql 数据库,java 的 oauth 官方工具包

建表

用户表 : 在授权方注册的第三方平台开发者或者公司

DROP TABLE IF EXISTS viewchaindb.open_user_account;
CREATE TABLE viewchaindb.open_user_account
(
    id          bigint(11)   NOT NULL AUTO_INCREMENT,
    user_name   varchar(255) NOT NULL COMMENT 'app 账户名称',
    user_type   tinyint(1)   NOT NULL comment '1-供应商 2-医院',
    app_key     varchar(255) NOT NULL COMMENT 'app 账户唯一id',
    app_secret  varchar(255) NOT NULL COMMENT 'app 密码',
    gmt_create  datetime     NOT NULL COMMENT '创建时间',
    gmt_modify  datetime              default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
    status      tinyint(1)   NOT NULL DEFAULT 0 COMMENT '状态 0:关闭  1:开启,2:禁用',
    ver         int(255)     NOT NULL DEFAULT 1,
    description varchar(255)          DEFAULT '' COMMENT '账户描述',
    PRIMARY KEY (id),
    KEY app_key (app_key),
    KEY user_type (user_type)
) ENGINE = InnoDB
  AUTO_INCREMENT = 0
  DEFAULT CHARSET = utf8mb4 COMMENT ='开放平台账户';

授权表 :在第三方平台操作的用户来授权服务后记录的绑定关系

DROP TABLE IF EXISTS viewchaindb.open_user_token;
CREATE TABLE viewchaindb.open_user_token
(
    id                        bigint(11) NOT NULL AUTO_INCREMENT,
    open_user_account_id      bigint(11) NOT NULL COMMENT '开放平台用户账户id',
    vh_user_id                bigint(11) NOT NULL COMMENT '内部用户id',
    auth_code                 varchar(255)        DEFAULT NULL COMMENT 'app 授权码',
    access_token              varchar(255)        DEFAULT NULL COMMENT '授权 token',
    refresh_token             varchar(255)        DEFAULT NULL COMMENT '刷新 token',
    token_expire_time         datetime            DEFAULT NULL COMMENT 'token 过期时间',
    refresh_token_expire_time datetime            DEFAULT NULL COMMENT 'refresh_token 过期时间',
    ver                       int(255)   NOT NULL DEFAULT 1,
    PRIMARY KEY (id),
    KEY vh_user_id (vh_user_id)
) ENGINE = InnoDB
  AUTO_INCREMENT = 0
  DEFAULT CHARSET = utf8mb4 COMMENT ='开放平台账户授权';

实现代码

工具包包装
package com.***.***.open.utils;

import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;

import java.util.UUID;

public class Oauth2Utils {

    public static String generateCode() throws OAuthSystemException {
        OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
        return oauthIssuerImpl.authorizationCode();
    }

    public static String generateAccessToken() throws OAuthSystemException {
        OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
        return oauthIssuerImpl.accessToken();
    }

    public static String generateRefreshToken() throws OAuthSystemException {
        OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
        return oauthIssuerImpl.refreshToken();
    }

    public static String generateAppKey() {
        UUID uuid = UUID.randomUUID();
        return uuid.toString();
    }

    public static String generateAppSecret() throws OAuthSystemException {
        UUID uuid = UUID.randomUUID();
        MD5Generator md5Generator = new MD5Generator();
        return md5Generator.generateValue(uuid.toString());
    }
}

实现方法

基础mybatis代码忽略


代码仅展示逻辑,声明、引包等操作都没有贴出,请根据命名自行推测字段含义。如有不明白的可以回复问我


1.授权码获取

/**
     * 用户根据分配的账号获取授权码(使用一次,未设置过期时间,如设置可用redis)
     * 授权码申请第一次保存到数据库 ,并返回给客户端
     *
     * @param request app_Key 账户key
     *                request app_secret  账户密码
     *                userId VIEWHIGH 用户id
     *                userName VIEWHIGH 用户名
     *                password VIEWHIGH 用户密码
     *                用户id和用户名密码不能同时为空
     * @return code 授权码
     */
    @Transactional
    GetAuthCodeResp getAuthorizationCode(GetAuthCodeReq request) throws OAuthSystemException {

        String appKey = request.getAppKey();
        String appSecret = request.getAppSecret();
        Long userId = request.getUserId();
        String userName = request.getUserName();
        String password = request.getPassword();
        log.info("请求授权,params:{}", request.toString());

        if ((StringUtils.isBlank(userName) || StringUtils.isBlank(password)) && userId <= 0L) {
            //todo  参数为空校验异常
            throw new CheckedException("user info cannot be null");
        }

        OpenUserAccount openUserAccount = openUserAccountMapper.queryByAppKey(appKey, appSecret);

        GetAuthCodeResp.Builder builder = GetAuthCodeResp.newBuilder();

        if (openUserAccount == null) {
            throw new CheckedException("No user info! Please check params( appKey and appSecret)");
        }

        String authCode = Oauth2Utils.generateCode();

        if (StringUtils.isNotBlank(authCode)) {

            OpenUserToken openUserToken = new OpenUserToken();
            openUserToken.setAuthCode(authCode);
            openUserToken.setOpenUserAccountId(openUserAccount.getId());
            openUserToken.setUserId(getUserId(userName, password, userId));
            openUserTokenMapper.addOpenUserToken(openUserToken);

            builder.setAuthCode(authCode);
        }

        //todo 失败编码

        return builder.build();
    }

2.授权码换token

/**
     * 根据授权码获取请求令牌 和 刷新令牌 并删除授权码
     * 后边刷新token根据刷新令牌来获取和延长时间
     * 刷新令牌失效需重新授权
     *
     * @param request appKey
     *                appSecret
     *                authCode  授权码
     * @return accessToken 请求令牌
     * refreshToken 刷新令牌
     */
    @Transactional
    GetAccessTokenResp getAccessToken(GetAccessTokenReq request) throws OAuthSystemException {

        String appKey = request.getAppKey();
        String authCode = request.getAuthCode();

        log.info("Get accessToken with code, params : {}", request.toString());

        OpenUserToken openUserToken = openUserTokenMapper.queryByCode(appKey, authCode, null);

        if (openUserToken == null) {
            throw new CheckedException("Code is Invalid");
        }

        GetAccessTokenResp.Builder builder = GetAccessTokenResp.newBuilder();

        String accessToken = Oauth2Utils.generateAccessToken();
        String refreshToken = Oauth2Utils.generateRefreshToken();

        if (StringUtils.isBlank(accessToken) || StringUtils.isBlank(refreshToken)) {
            throw new CheckedException("System exception! Cannot generate token! Please try again later.");
        }

        Date accessTokenExpiredTime = DateUtils.addSecond(new Date(), TEN_HOURS_MILLS);
        Date refreshTokenExpiredTime = DateUtils.addDay(new Date(), HALF_YEAR_DAYS);

        builder.setAccessToken(accessToken)
                .setRefreshToken(refreshToken)
                .setAccessTokenExpiredTime(
                        DateUtils.format(
                                accessTokenExpiredTime, FMT_DATETIME_WHOLE))
                .setRefreshTokenExpiredTime(
                        DateUtils.format(
                                refreshTokenExpiredTime, FMT_DATETIME_WHOLE));

        openUserToken.setAccessToken(accessToken);
        openUserToken.setRefreshToken(refreshToken);
        openUserToken.setTokenExpireTime(accessTokenExpiredTime);
        openUserToken.setRefreshTokenExpireTime(refreshTokenExpiredTime);
        openUserToken.setAuthCode(null);

        openUserTokenMapper.updateOpenUserToken(openUserToken);

        return builder.build();
    }

3.刷新token

/**
     * 刷新令牌,如果请求令牌未过期则重置失效时间,
     * 如果请求令牌过期,刷新令牌未过期则重新生成新令牌
     * 刷新令牌过期需要重新授权
     *
     * @param request
     * @return
     */
    @Transactional
    RefreshTokenResp refreshToken(RefreshTokenReq request) throws OAuthSystemException {

        String appKey = request.getAppKey();
        String refreshToken = request.getRefreshToken();

        log.info("Refresh access token ,params : {}", request.toString());

        if (StringUtils.isBlank(appKey)) {
            throw new CheckedException("AppKey cannot be null");
        }

        if (StringUtils.isBlank(refreshToken)) {
            throw new CheckedException("RefreshToken cannot be null");
        }

        RefreshTokenResp.Builder builder = RefreshTokenResp.newBuilder();

        OpenUserToken openUserToken = openUserTokenMapper.queryByCode(appKey, null, refreshToken);

        if (openUserToken == null) {
            throw new CheckedException("RefreshToken do not exist or appKey did not authorized");
        }

        if (openUserToken.getRefreshTokenExpireTime() == null
                || !openUserToken.getRefreshTokenExpireTime().after(new Date())) {
            throw new CheckedException("RefreshToken is expired! Please retry authorize");
        }

        if (StringUtils.isBlank(openUserToken.getAccessToken()) || openUserToken.getTokenExpireTime() == null) {
            throw new CheckedException("AccessToken has not been generated! Please retry authorized token");
        }

        Date accessTokenExpiredTime = DateUtils.addSecond(new Date(), TEN_HOURS_MILLS);

        String accessToken = openUserToken.getAccessToken();

        Date date = new Date();

        if (openUserToken.getTokenExpireTime().before(date)) {
            accessToken = Oauth2Utils.generateAccessToken();
        }

        openUserToken.setAccessToken(accessToken);
        openUserToken.setTokenExpireTime(accessTokenExpiredTime);
        openUserTokenMapper.updateOpenUserToken(openUserToken);

        builder.setAccessToken(accessToken)
                .setAccessTokenExpiredTime(DateUtils.format(
                        accessTokenExpiredTime, FMT_DATETIME_WHOLE))
                .setRefreshToken(refreshToken)
                .setRefreshTokenExpiredTime(DateUtils.format(openUserToken.getRefreshTokenExpireTime(), FMT_DATETIME_WHOLE));

        return builder.build();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值