Spring Social总结(一)

OAuth协议

含义

发放给用户一个令牌,用户通过令牌来访问数据.

流程

Spring Social总结(一)_第1张图片

授权模式

  • 授权码模式
  • 简化模式
  • 密码模式
  • 客户端模式

授权码模式

Spring Social总结(一)_第2张图片

SpringSocial实现第三方登陆
基本原理

当用户使用授权码模式拿到令牌之后,可以在服务器上获取用户的基本信息,然后根据用户信息构建Authentication,并放入SecurityContext,此时对于SpringSecurity已经登陆成功.
Spring Social总结(一)_第3张图片

具体实现

Spring Social总结(一)_第4张图片

  • ServiceProvider(AbstractOAuth2ServiceProvider):服务提供商的抽象,针对每个具体的服务提供商(比如qq,微信等)提供的接口的实现,并提供抽象类AbstractOAuth2ServiceProvider,我们实现这个抽象类

    • OAuth2Operations(OAuth2Template):OAuth协议相关的操作,OAuth2Template完成OAuth协议的执行流程
    • api(AbstractOAuth2ApiBinding):实现获取用户信息
  • Connection(OAuth2Connection):封装之前获取到的用户信息,实际实现类为OAuth2Connection
  • ConnectionFactory(OAuth2ConnectionFactory):负责创建OAuth2Connection

    • ServiceProvider:封装了前面所有的流程,通过调用ServiceProvider走前面所有的流程,走通之后通过获取到的用户数据创建OAuth2Connection对象
    • ApiAdapter:不同的服务提供商具有不同的用户数据结构,ApiAdapter主要负责将不同的数据猎狗转换成Connection标准的数据结构
  • UserConnection:在数据库中,将我们业务的用户于服务提供商的用户建立对应关系
  • UserConnectionRepository(JdbcUserConnectionRepository):负责操作UserConnection的对应关系,JdbcUserConnectionRepository负责针对UserConnection表中的数据做增删改查的操作

QQ登录

封装QQ登录返回的用户数据

@Data
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;
}

建立获取用户信息的接口

public interface QQ {
    QQUserInfo getUserInfo();
}

建立QQ接口的实现类,并继承AbstractOAuth2ApiBinding抽象类,

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;
    priate String openId;
    public QQImpl(String accessToken, String appId) {
        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 = String.valueOf(JSONUtil.parseObj(result).get("openid"));
    }
    /**
     * 获取用户信息
     *
     * @return
     */
    @Override
    public QQUserInfo getUserInfo() {
        String url = String.format(URL_GET_USERINFO, appId, openId);
        String result = getRestTemplate().getForObject(url, String.class);
        System.out.println(result);
        QQUserInfo userInfo = null;
        try {
            userInfo = JSONUtil.toBean(result, QQUserInfo.class);
            userInfo.setOpenId(openId);
            return userInfo;
        } catch (Exception e) {
            throw new RuntimeException("获取用户信息失败", e);
        }
    }
}

实现ServiceProvider

public class QQServiceProvider extends AbstractOAuth2ServiceProvider {
    private String appId;
    private static final String URL_AUTHORIZE = "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) {
        super(new OAuth2Template(appId, appSecret, URL_AUTHORIZE, URL_ACCESS_TOKEN));
    }
    @Override
    public QQ getApi(String accessToken) {
        return new QQImpl(accessToken, appId);
    }
}

实现ApiAdapter

public class QQAdapter implements ApiAdapter {
    //发出请求判断请求是不是通的,true为是通的,不发出请求
    @Override
    public boolean test(QQ qq) {
        return true;
    }
    //适配onnection数据和api数据
    @Override
    public void setConnectionValues(QQ qq, ConnectionValues values) {
        QQUserInfo userInfo = qq.getUserInfo();
        values.setDisplayName(userInfo.getNickname());
        values.setImageUrl(userInfo.getFigureurl_qq_1());
        values.setProfileUrl(null);
        values.setProviderUserId(userInfo.getOpenId());
    }
    @Override
    public UserProfile fetchUserProfile(QQ qq) {
        return null;
    }
    @Override
    public void updateStatus(QQ qq, String s) {
    }
}

实现ConnectionFactory

public class QQConnectionFactory extends OAuth2ConnectionFactory {
    public QQConnectionFactory(String providerId, String appId, String appSecret) {
        super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter());
    }
}

qq登录的各项配置

public class QQProperties extends SocialProperties {
    private String providerId = "qq";
    public String getProviderId() {
        return providerId;
    }
    public void setProviderId(String providerId) {
        this.providerId = providerId;
    }
}

注:springboot2.0在org.springframework.boot.autoconfigure里没有social相关的配置简化类。直接将SocialAutoConfigurerAdapter、SocialProperties和SocialWebAutoConfiguration这三个类拷贝到项目里
将QQProperties添加到整体的SecurityProperties中

public class SocialProperties {
    private QQProperties qq = new QQProperties();
}

你可能感兴趣的:(springsecurity,oauth)