Spring Social实现OAuth--- QQ授权认证Demo

package com.zcw.demospringsecurity.demo16;

import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName : JacksonFromTextHtmlHttpMessageConverter
 * @Description :添加对text/html支持
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 14:32
 */
public class JacksonFromTextHtmlHttpMessageConverter extends MappingJackson2HttpMessageConverter {
    public JacksonFromTextHtmlHttpMessageConverter(){
        List mediaTypes = new ArrayList();
        mediaTypes.add(MediaType.TEXT_HTML);
        setSupportedMediaTypes(mediaTypes);
    }
}

package com.zcw.demospringsecurity.demo16;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
 * @ClassName : MYQQUserInfo
 * @Description :针对《SpringSecurity实战(陈木鑫)》授权一章demo练习
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 14:15
 */
@Data
public class MYQQUserInfo {


    private String nickname;
    @JsonInclude("figureurl")
    private String figureUrl30;
    @JsonProperty("figureurl_1")
    private String figureUrl50;
    @JsonProperty("figureurl_2")
    private String figureUrl100;

    private String gender;

    @JsonProperty("figureurl_qq_1")
    private String qqFigureUrl40;

    @JsonProperty("figureurl_qq_2")
    private String qqFigureUrl100;

    private String openId;
}

package com.zcw.demospringsecurity.demo16;

/**
 * @ClassName : QQ
 * @Description : 创建API接口
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 14:35
 */
public interface QQ {
    MYQQUserInfo getUserInfo();
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.ConnectionValues;
import org.springframework.social.connect.UserProfile;

/**
 * @ClassName : QQAdapter
 * @Description : 创建适配器
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 16:52
 */
public class QQAdapter implements ApiAdapter<QQ> {

    @Override
    public boolean test(QQ qq){
        return  true;
    }
    //通过API获取用户信息,并填充到Connection对象中
    @Override
    public void setConnectionValues(QQ qq, ConnectionValues connectionValues){
        MYQQUserInfo myqqUserInfo = qq.getUserInfo();
        connectionValues.setProviderUserId(myqqUserInfo.getOpenId());
        connectionValues.setDisplayName(myqqUserInfo.getNickname());
        connectionValues.setImageUrl(myqqUserInfo.getQqFigureUrl100());
        connectionValues.setProfileUrl(null);
    }
    @Override
    public UserProfile fetchUserProfile(QQ qq){
        return  null;
    }
    @Override
    public void updateStatus(QQ qq,String s){

    }
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName : QQConfig
 * @Description : 创建配置类
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 17:08
 */
@Configuration
@ConfigurationProperties("spring.social.qq")
public class QQConfig {
    private String providerId="qq";
    private String appId;
    private String appSecret;

    public String getAppId(){
        return appId;
    }
    public void setAppId(String appId){
        this.appId = appId;
    }
    public String getAppSecret(){
        return appSecret;
    }
    public void setAppSecret(String appSecret){
        this.appSecret = appSecret;
    }
    public String getProviderId(){
        return providerId;
    }
    public void setProviderId(String providerId){
        this.providerId = providerId;
    }
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.social.connect.support.OAuth2ConnectionFactory;

/**
 * @ClassName : QQConnectionFactory
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 17:01
 */
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
    public QQConnectionFactory(String providerId,String appId,String appSecret ){
        super(providerId,new QQServiceProvider(appId,appSecret),new QQAdapter());
    }
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.web.client.RestTemplate;

/**
 * @ClassName : QQImpl
 * @Description :继承AbstractOAuth2ApiBinding,完成API接口与spring Social的捆绑
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 14:36
 */
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
    //获取openId的API
    private static final String QQ_URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token={accessToken}";
    //获取用户信息的API
    private static final String QQ_URL_GET_USER_INFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key={appId}&openid={openId}";

    private String appId;

    private String openId;

    //构造函数在accessToken获取之后被调用,此处可以初始化openID
    public QQImpl(String accessToken,String appId){
        //accwss_token 作为查询参数被携带
        //获取用户信息接口,无须声明携带accessToken,所有资源交互接口都会自行携带
        this.appId = appId;
        //获取openID接口响应
        String result = getRestTemplate().getForObject(QQ_URL_GET_OPENID,String.class,accessToken);
        //提取openid
        this.openId = result.substring(result.lastIndexOf(":\"")+2,result.indexOf("\"}"));
    }

    @Override
    public MYQQUserInfo getUserInfo(){
        MYQQUserInfo myqqUserInfo = getRestTemplate().getForObject(QQ_URL_GET_USER_INFO,MYQQUserInfo.class,appId,
                openId);
        //为用户信息类补充openId
        if(myqqUserInfo !=null){
            myqqUserInfo.setOpenId(openId);
        }

        return myqqUserInfo;
    }
    @Override
    protected void configureRestTemplate(RestTemplate restTemplate){
        //为API交互的RestTemplate实例添加  text/heml 转JSON对象的支持
        restTemplate.getMessageConverters().add(new JacksonFromTextHtmlHttpMessageConverter());
    }
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.social.connect.ConnectionFactory;

/**
 * @ClassName : QQOAuth2Config
 * @Description : 创建QQ专用连接工厂
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 17:13
 */
@Configuration
public class QQOAuth2Config  extends SocialAutoConfigurerAdapter{
    @Autowired
    private QQConfig qqConfig;

    @Override
    protected ConnectionFactory<?> createConnectionFactory() {
        return new QQConnectionFactory(qqConfig.getProviderId(),qqConfig.getAppId(),qqConfig.getAppSecret());
    }
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.oauth2.OAuth2Template;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

/**
 * @ClassName : QQOAuth2Template
 * @Description : 创建QQService Provider
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 14:52
 */
public class QQOAuth2Template extends OAuth2Template {
    /**
     * 《SpringSecurity实战(陈木鑫)》
     * 书中写到,QQServiceProvider包含OAuth2Template和API捆绑接口
     * 两个部件,所以在创建QQServiceProvider之前,应当实现OAuth2Template
     */
    public QQOAuth2Template(String clientId,String clientSecret,String authorizeUrl,String accessTokenUrl){
        super(clientId,clientSecret,authorizeUrl,accessTokenUrl);
        //指明获取accessToken时,需要携带client_id和client_secret两个参数,(对应申请qq互联是的appid和appKey)
        setUseParametersForClientAuthentication(true);
    }

    @Override
    protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String,String> parameters){
        //获取accessToken接口响应
        String responseStr = getRestTemplate().postForObject(accessTokenUrl,parameters,String.class);
        //从API文档中可以获知解析accessToken的方式
        String[]items = responseStr.split("&");
        //http://wiki.connect.qq.com/使用authorization_code获取access_token
        String accessToken = items[0].substring(items[0].lastIndexOf("=")+1);
        Long expiresIn = new Long(items[1].substring(items[1].lastIndexOf("=") +1));
        String refreshToken = items[2].substring(items[2].lastIndexOf("=")+1);
        return new AccessGrant(accessToken,null,refreshToken,expiresIn);
    }
    /**
     * 处理text/html类型的数据
     */
    @Override
    protected RestTemplate createRestTemplate(){
        RestTemplate restTemplate =super.createRestTemplate();
        restTemplate.getMessageConverters().add(
                new TextHtmlHttpMessageConverter()
        );
        return restTemplate;
    }
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;

/**
 * @ClassName : QQServiceProvider
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 16:43
 */
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {

    //设置跳转QQ快捷登录页面路径
    private static final String QQ_URL_AUTHORIZE ="https://graph.qq.com/oauth2.0/authorize";
    //获取accessToken接口
    private static final String QQ_URL_ACCESS_TOKEN="https://graph.qq.com/oauth2.0/token";

    private String appId;
    //设置OAuth2Template的实现类
    public QQServiceProvider(String appId,String appSecret){
        super(new QQOAuth2Template(appId,appSecret,QQ_URL_AUTHORIZE,QQ_URL_ACCESS_TOKEN));
        this.appId = appId;
    }
    //声明API捆绑部件
    @Override
    public QQ getApi(String accessToken){
        return new QQImpl(accessToken,appId);
    }
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.core.env.Environment;
import org.springframework.social.config.annotation.ConnectionFactoryConfigurer;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.ConnectionFactory;

/**
 * @ClassName : SocialAutoConfigurerAdapter
 * @Description : 重写此类
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 18:16
 */
public abstract class SocialAutoConfigurerAdapter extends SocialConfigurerAdapter {

    public SocialAutoConfigurerAdapter() {
    }
    public void addConnectionFactories(ConnectionFactoryConfigurer configurer, Environment environment) {
        configurer.addConnectionFactory(this.createConnectionFactory());
    }
    protected abstract ConnectionFactory<?> createConnectionFactory();
}

package com.zcw.demospringsecurity.demo16;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.nio.charset.Charset;

/**
 * @ClassName : TextHtmlHttpMessageConverter
 * @Description : 添加RestTemplate解析模板
 * @Author : Zhaocunwei
 * @Date: 2020-04-13 14:20
 */
public class TextHtmlHttpMessageConverter extends AbstractHttpMessageConverter {
    /**
     * 《SpringSecurity实战(陈木鑫)》
     * 陈木鑫老师说:
     *      与QQ接口的交互上,响应类型都为text/html形式,RestTemplate默认不支持该响应类型,
     *      所以应当自行添加,
     *      主要有两个类:
     *      一类是:text/html转普通文本
     *      另一类: text/html转json对象
     */

    public TextHtmlHttpMessageConverter(){
        super(Charset.forName("UTF-8"),new MediaType[]{MediaType.TEXT_HTML});
    }

    @Override
    protected boolean supports(Class clazz){
        return String.class == clazz;
    }

    @Override
    protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage)
            throws IOException, HttpMessageNotReadableException{
        Charset   charset = this.getContentTypeCharset(httpInputMessage.getHeaders().getContentType());
        return StreamUtils.copyToString(httpInputMessage.getBody(),charset);
    }
    private Charset getContentTypeCharset(MediaType contentType){
        return contentType !=null && contentType.getCharset() !=null ?contentType.getCharset():this.getDefaultCharset();
    }

    @Override
    protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage)
        throws IOException, HttpMessageNotWritableException {

    }

}

发布了475 篇原创文章 · 获赞 16 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_32370913/article/details/105572759
今日推荐