微信第三方登录

最近业务需要,对接了微信,QQ,的第三方登录,下面以微信为例,总结下第三方登录的流程与使用到的技术

一.用的核心的技术和规范:

  - SpringBoot 2.2.6.RELEASE

  - SpringCloud Nacos(由于整个项目是微服务项目,所以有用到其中的很多组件)

  - 第三方依赖 JustAuth  1.15.9 (这个轮子很好用,整合了市面上大多数的第三方认证授权)链接: https://github.com/justauth/JustAuth?utm_source=gold_browser_extension

      - Oauth2.0认证标准规范

Oauth2.0认证流程(以微信 网站应用 为例),简单介绍一下Oauth2.0流程

      - 文档地址: https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

      - 微信Oauth2.0认证时序图:

  

   从时序图中可以看到第4步用户扫码确认后,第5步是微信开放平台回调到网站应用,回调域名配置在微信开放平台 授权回调域,表示用户扫码后只能回调到当前配置的域名下面

   

  

        

二.后端代码

  1.获取微信二维码链接的接口:

package com.leigod.center.user.nnpc.config;

import lombok.Data;
import me.zhyd.oauth.config.AuthConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 第三方平台AppId ,AppSecret配置类
 * @author Sam.yang
 * @since 2021/1/29 17:38
 */
@Data
@Component
@ConfigurationProperties(prefix = "oauth2")
public class OAuthProperties {

    /**
     * QQ 配置
     */
    private AuthConfig qq;

    /**
     * github 配置
     */
    private AuthConfig github;

    /**
     * 微信 配置
     */
    private AuthConfig wechatOpen;

    /**
     * Google 配置
     */
    private AuthConfig google;

    /**
     * Microsoft 配置
     */
    private AuthConfig microsoft;

    /**
     * Mi 配置
     */
    private AuthConfig mi;
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package me.zhyd.oauth.config;

import com.xkcoding.http.config.HttpConfig;
import java.util.List;
/**
 * 配置类JustAuth 自带的
 */
public class AuthConfig {
    private String clientId;
    private String clientSecret;
    private String redirectUri;
    private String alipayPublicKey;
    private boolean unionId;
    private String stackOverflowKey;
    private String agentId;
    private String codingGroupName;
    private HttpConfig httpConfig;
    private boolean ignoreCheckState;
    private List<String> scopes;
    private String deviceId;
    private Integer clientOsType;
    private String packId;

    public static AuthConfig.AuthConfigBuilder builder() {
        return new AuthConfig.AuthConfigBuilder();
    }

    public String getClientId() {
        return this.clientId;
    }

    public String getClientSecret() {
        return this.clientSecret;
    }

    public String getRedirectUri() {
        return this.redirectUri;
    }

    public String getAlipayPublicKey() {
        return this.alipayPublicKey;
    }

    public boolean isUnionId() {
        return this.unionId;
    }

    public String getStackOverflowKey() {
        return this.stackOverflowKey;
    }

    public String getAgentId() {
        return this.agentId;
    }

    public String getCodingGroupName() {
        return this.codingGroupName;
    }

    public HttpConfig getHttpConfig() {
        return this.httpConfig;
    }

    public boolean isIgnoreCheckState() {
        return this.ignoreCheckState;
    }

    public List<String> getScopes() {
        return this.scopes;
    }

    public String getDeviceId() {
        return this.deviceId;
    }

    public Integer getClientOsType() {
        return this.clientOsType;
    }

    public String getPackId() {
        return this.packId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    public void setRedirectUri(String redirectUri) {
        this.redirectUri = redirectUri;
    }

    public void setAlipayPublicKey(String alipayPublicKey) {
        this.alipayPublicKey = alipayPublicKey;
    }

    public void setUnionId(boolean unionId) {
        this.unionId = unionId;
    }

    public void setStackOverflowKey(String stackOverflowKey) {
        this.stackOverflowKey = stackOverflowKey;
    }

    public void setAgentId(String agentId) {
        this.agentId = agentId;
    }

    public void setCodingGroupName(String codingGroupName) {
        this.codingGroupName = codingGroupName;
    }

    public void setHttpConfig(HttpConfig httpConfig) {
        this.httpConfig = httpConfig;
    }

    public void setIgnoreCheckState(boolean ignoreCheckState) {
        this.ignoreCheckState = ignoreCheckState;
    }

    public void setScopes(List<String> scopes) {
        this.scopes = scopes;
    }

    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
    }

    public void setClientOsType(Integer clientOsType) {
        this.clientOsType = clientOsType;
    }

    public void setPackId(String packId) {
        this.packId = packId;
    }

    public AuthConfig() {
    }

    public AuthConfig(String clientId, String clientSecret, String redirectUri, String alipayPublicKey, boolean unionId, String stackOverflowKey, String agentId, String codingGroupName, HttpConfig httpConfig, boolean ignoreCheckState, List<String> scopes, String deviceId, Integer clientOsType, String packId) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.redirectUri = redirectUri;
        this.alipayPublicKey = alipayPublicKey;
        this.unionId = unionId;
        this.stackOverflowKey = stackOverflowKey;
        this.agentId = agentId;
        this.codingGroupName = codingGroupName;
        this.httpConfig = httpConfig;
        this.ignoreCheckState = ignoreCheckState;
        this.scopes = scopes;
        this.deviceId = deviceId;
        this.clientOsType = clientOsType;
        this.packId = packId;
    }

    public static class AuthConfigBuilder {
        private String clientId;
        private String clientSecret;
        private String redirectUri;
        private String alipayPublicKey;
        private boolean unionId;
        private String stackOverflowKey;
        private String agentId;
        private String codingGroupName;
        private HttpConfig httpConfig;
        private boolean ignoreCheckState;
        private List<String> scopes;
        private String deviceId;
        private Integer clientOsType;
        private String packId;

        AuthConfigBuilder() {
        }

        public AuthConfig.AuthConfigBuilder clientId(String clientId) {
            this.clientId = clientId;
            return this;
        }

        public AuthConfig.AuthConfigBuilder clientSecret(String clientSecret) {
            this.clientSecret = clientSecret;
            return this;
        }

        public AuthConfig.AuthConfigBuilder redirectUri(String redirectUri) {
            this.redirectUri = redirectUri;
            return this;
        }

        public AuthConfig.AuthConfigBuilder alipayPublicKey(String alipayPublicKey) {
            this.alipayPublicKey = alipayPublicKey;
            return this;
        }

        public AuthConfig.AuthConfigBuilder unionId(boolean unionId) {
            this.unionId = unionId;
            return this;
        }

        public AuthConfig.AuthConfigBuilder stackOverflowKey(String stackOverflowKey) {
            this.stackOverflowKey = stackOverflowKey;
            return this;
        }

        public AuthConfig.AuthConfigBuilder agentId(String agentId) {
            this.agentId = agentId;
            return this;
        }

        public AuthConfig.AuthConfigBuilder codingGroupName(String codingGroupName) {
            this.codingGroupName = codingGroupName;
            return this;
        }

        public AuthConfig.AuthConfigBuilder httpConfig(HttpConfig httpConfig) {
            this.httpConfig = httpConfig;
            return this;
        }

        public AuthConfig.AuthConfigBuilder ignoreCheckState(boolean ignoreCheckState) {
            this.ignoreCheckState = ignoreCheckState;
            return this;
        }

        public AuthConfig.AuthConfigBuilder scopes(List<String> scopes) {
            this.scopes = scopes;
            return this;
        }

        public AuthConfig.AuthConfigBuilder deviceId(String deviceId) {
            this.deviceId = deviceId;
            return this;
        }

        public AuthConfig.AuthConfigBuilder clientOsType(Integer clientOsType) {
            this.clientOsType = clientOsType;
            return this;
        }

        public AuthConfig.AuthConfigBuilder packId(String packId) {
            this.packId = packId;
            return this;
        }

        public AuthConfig build() {
            return new AuthConfig(this.clientId, this.clientSecret, this.redirectUri, this.alipayPublicKey, this.unionId, this.stackOverflowKey, this.agentId, this.codingGroupName, this.httpConfig, this.ignoreCheckState, this.scopes, this.deviceId, this.clientOsType, this.packId);
        }

        public String toString() {
            return "AuthConfig.AuthConfigBuilder(clientId=" + this.clientId + ", clientSecret=" + this.clientSecret + ", redirectUri=" + this.redirectUri + ", alipayPublicKey=" + this.alipayPublicKey + ", unionId=" + this.unionId + ", stackOverflowKey=" + this.stackOverflowKey + ", agentId=" + this.agentId + ", codingGroupName=" + this.codingGroupName + ", httpConfig=" + this.httpConfig + ", ignoreCheckState=" + this.ignoreCheckState + ", scopes=" + this.scopes + ", deviceId=" + this.deviceId + ", clientOsType=" + this.clientOsType + ", packId=" + this.packId + ")";
        }
    }
}
/**
 * 网页应用第三方登录
 *
 * @author Sam.yang
 * @since 2021/1/29 16:28
 */
@Slf4j
@RestController
@RequestMapping(value = "/api/oauth")
public class ApiAuthOpenController {


    @Autowired
    private OAuthProperties properties;


    /**
     * 重定向到微信
     *
     * @param oauthType 认证类型
     * @throws IOException
     */
    @PostMapping("/render/{oauthType}")
    @ApiOperation(value = "第三方账号登录")
    public BaseOutput<String> renderAuth(@PathVariable("oauthType") String oauthType) throws IOException {

        AuthRequest authRequest = this.getAuthRequest(oauthType);
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
        log.info("第三方登录 url:[{}]", authorizeUrl);

        return BaseOutput.success(authorizeUrl);
    }
    

    /**
     * 构建请求对象
     *
     * @param oauthType 第三方登录类型
     * @return {@link AuthRequest}
     */
    private AuthRequest getAuthRequest(String oauthType) {
        AuthDefaultSource authSource = AuthDefaultSource.valueOf(oauthType.toUpperCase());
        switch (authSource) {
            case QQ:
                return this.getQqAuthRequest();
            case WECHAT_OPEN:
                return this.getWechatAuthRequest();
            default:
                throw new RuntimeException("暂不支持的第三方登录");
        }
    }

    /**
     * 构建QQ 认证请求对象
     *
     * @return {@link AuthRequest}
     */
    private AuthRequest getQqAuthRequest() {

        AuthConfig authConfig = properties.getQq();
        return new AuthQqRequest(authConfig);
    }

    /**
     * 构建微信 认证请求对象
     *
     * @return {@link AuthRequest}
     */
    private AuthRequest getWechatAuthRequest() {
        AuthConfig authConfig = properties.getWechatOpen();
        return new AuthWeChatOpenRequest(authConfig);
    }
}
 /**
     * 获取第三方UnionID
     *
     * @param userOpen {@link UserOpen}
     * @return UnionID
     */
    private UserOpen getUnionID(UserOpen userOpen) {
        AuthCallback callback = AuthCallback.builder().code(userOpen.getCode()).state(userOpen.getState())
                .build();

        AuthRequest authRequest = this.getAuthRequest(AuthEnum.valueOf(userOpen.getOpenType().toUpperCase())
                .getDesc().toUpperCase());

        log.info("第三方登录请求参数:[{}]", JSON.toJSONString(authRequest));
        AuthResponse response = authRequest.login(callback);
        if (!response.ok()) {
            log.info("获取用户信息失败 response:[{}]", JSON.toJSONString(response));
            throw new BaseException(RetCode.ERROR);
        }
        log.info("第三方登录结果:[[}]", JSON.toJSONString(response));

        AuthUser user = (AuthUser) response.getData();
        AuthToken token = user.getToken();
        userOpen.setUnionId(token.getUnionId());
        return userOpen;
    }
原文地址:https://www.cnblogs.com/july-sunny/p/14363485.html