转载

19.SpringSecurity-开发QQ登录-ServiceProvider开发

前言

19.SpringSecurity-开发QQ登录-ServiceProvider开发

  1. 开发QQ登录 实现QQ登录,关键是走一个oauth2流程,拿到服务上提供的授权码,然后获取到令牌,最后拿到qq的登录信息。服务提供商的用户信息是封装在Connection里面,所以我们首先需要拿到一个Connection;我们需要创建一个Connection,那么我们就需要创建一个ConnectionFactory工厂。我们构建ConnectionFactory工厂需要两个东西,一个服务提供商:ServiceProvider,另一个是ApiAdapter。我们要拿到ServiceProvider,那么我们就需要实现:OAuth2Operations(负责前1-6步走oauth流程)接口和Api(负责7.获取用户信息)接口的实现。
  2. 我们开发步骤是:先写Api这个获取用户信息的实现,然后有了Api,我用OAuth2Operations其默认的OAuth2Template来构建ServiceProvider。构建出ServiceProvider之后我们实现ApiAdapter;ApiAdapter和ServiceProvider都有了,然后我们就可以构建ConnectionFactory工厂;进而拿到用户信息;有了用户信息之后,我们回去数据库创建一张UserConnection表,配置JdbcUsersConnectionRepository去操作这张表。

内容

由于QQ登录也可以在web浏览器和app端去使用,所以我们将其放在spring-security-core里面。

1.开发Api模块:获取用户信息

1.1 基本功能开发

我们查看AbstractOAuth2ApiBinding的属性:

private final String accessToken;
private RestTemplate restTemplate;
  1. 里面有一个accessToken:就是存储前5步完成以后获取的令牌,这里要注意每一个人走完流程获取到的令牌都是不一样的,但是accessToken是一个类级别的全局变量,所以我们继承了AbstractOAuth2ApiBinding类的QQImpl不是一个单例对象,而是一个多实例对象。
  2. restTemplate就是帮我们去发送http请求的。
  3. 通过以上属性,其实我们可以知道qq相关信息已经在我们AbstractOAuth2ApiBinding里面了。

1.2 获取qq信息开发

我们查看获取qq用户信息文档: https://wiki.connect.qq.com/ 查看里面的API文档,获取里面的Api列表。

19.SpringSecurity-开发QQ登录-ServiceProvider开发

19.SpringSecurity-开发QQ登录-ServiceProvider开发

19.SpringSecurity-开发QQ登录-ServiceProvider开发

19.SpringSecurity-开发QQ登录-ServiceProvider开发

19.SpringSecurity-开发QQ登录-ServiceProvider开发

1.3 代码

  1. 1.QQUserInfo
public class QQUserInfo {

    /**
     *     返回码
     */
    private String ret;
    /**
     * 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
     */
    private String msg;
    /**
     *
     */
    private String openId;
    /**
     * 不知道什么东西,文档上没写,但是实际api返回里有。
     */
    private String is_lost;
    /**
     * 省(直辖市)
     */
    private String province;
    /**
     * 市(直辖市区)
     */
    private String city;
    /**
     * 出生年月
     */
    private String year;
    /**
     *     用户在QQ空间的昵称。
     */
    private String nickname;
    /**
     *     大小为30×30像素的QQ空间头像URL。
     */
    private String figureurl;
    /**
     *     大小为50×50像素的QQ空间头像URL。
     */
    private String figureurl_1;
    /**
     *     大小为100×100像素的QQ空间头像URL。
     */
    private String figureurl_2;
    /**
     *     大小为40×40像素的QQ头像URL。
     */
    private String figureurl_qq_1;
    /**
     *     大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100×100的头像,但40×40像素则是一定会有。
     */
    private String figureurl_qq_2;
    /**
     *     性别。 如果获取不到则默认返回”男”
     */
    private String gender;
    /**
     *     标识用户是否为黄钻用户(0:不是;1:是)。
     */
    private String is_yellow_vip;
    /**
     *     标识用户是否为黄钻用户(0:不是;1:是)
     */
    private String vip;
    /**
     *     黄钻等级
     */
    private String yellow_vip_level;
    /**
     *     黄钻等级
     */
    private String level;
    /**
     * 标识是否为年费黄钻用户(0:不是; 1:是)
     */
    private String is_yellow_year_vip;
    
    //getter setter
  1. 2.QQ接口
public interface QQ {
    /**
     * 获取QQ信息
     */
    QQUserInfo getUserInfo();
}
  1. 3.QQ接口实现类
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
    private static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
    private static final String URL_GET_USERINFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";

    private String appId;

    private String openId;

    private ObjectMapper objectMapper = new ObjectMapper();

    public QQImpl(String accessToken, String appId) {
        //设置accessToken是在url参数上传递,默认在header里面
        super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
        this.appId = appId;

        String url = String.format(URL_GET_OPENID, accessToken);
        String result = getRestTemplate().getForObject(url, String.class);

        System.out.println(result);
        this.openId = StringUtils.substringBetween(result, "/"openid/":/"", "/"}");
    }
    @Override
    public QQUserInfo getUserInfo() {
        String url = String.format(URL_GET_USERINFO, appId, openId);
        String result = getRestTemplate().getForObject(url, String.class);
        System.out.println(result);
        try {
            return objectMapper.readValue(result,QQUserInfo.class);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("获取用户信息失败", e);
        }
    }
}

2.QQServiceProvider

新建包:com.yxm.security.core.connect,创建类:

继承接口时候:需要返回一个接口泛型:这个接口泛型是前面讲的Api模块的实现类的接口;这里面指代qq。

AbstractOAuth2ServiceProvider<QQ>

QQServiceProvider代码:

public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {
    private String appId;
    /**
     *应用将用户导向认证服务器时候,导向的url;
     * https://wiki.connect.qq.com/的qq互联:网站应用-->获取Access_Token->Step1:获取Authorization Code的url:PC网站:https://graph.qq.com/oauth2.0/authorize
     */
    private static final String URL_AUTHORIZA="https://graph.qq.com/oauth2.0/authorize";
    private static final String URL_ACCESS_TOKEN="https://graph.qq.com/oauth2.0/token";
    public QQServiceProvider(String appId,String appSecret) {
        /**
         * 1.我们从开发流程图上看到:ServiceProvider构建需要OAuth2Operations
         * 2.我们在qq互联上注册我们的应用时候,clientId,clientSecret其实就是qq提供给第三方应用的appId,appSecret,相当于app
         * 的用户名/密码。
         * 3.authorizeUrl,accessTokenUr对应于我们SpringSocial基本原理里面的第一步和第四步。
         * authorizeUrl---应用将用户导向认证服务器时候,导向的url是什么。
         * accessTokenUr---第四部我们申请token时候携带的地址
         */
        super(new OAuth2Template(appId,appSecret,URL_AUTHORIZA,URL_ACCESS_TOKEN));
    }
    @Override
    public QQ getApi(String accessToken) {
        /**
         * 1.我们需要获取Api的时候:由于要求是accessToken是多值得,所以QQImpl是不能通过注解:@Component来注解的
         * 因为final修饰的成员变量是不可变得,一个对象有一个成员变量,是全局的,我们这里用new
         * 2.接受的2个参数:
         * accessToken抽象类会传给我们,我们不用管。
         * appId,需要我们自己处理了:所以我们需要自己传一个,由于appId指代qq提供的id,他是唯一的。
         */
        return new QQImpl(accessToken,appId);
    }
}

你好 QQServiceProvider完成了的话,相当于我们右边部分已经完成了,服务提供商相关代码完成了。

到此,QQ模块功能如下:

19.SpringSecurity-开发QQ登录-ServiceProvider开发

原文  https://segmentfault.com/a/1190000022055584
正文到此结束
Loading...