Spring Social开发QQ第三方登录

OAuth授权协议简介

OAuth协议要解决的问题

解决第三方应用的访问授权问题,用户不需要通过密码进行授权,只需要通过token授权即可,保护了密码泄露的问题。

OAuth协议中的各种角色

在这里插入图片描述
服务提供商Provider:提供令牌Token。

  • 认证服务器:Authentication Server 认证用户身份,产生令牌。
  • 资源服务器:Resource Server 保存用户信息,验证令牌。
    资源所有者Resource Owner: 用户
    第三方应用Client:比如qq登录、微信登录。

OAuth协议中的授权模式有四种:

  • 授权码模式 (authentication code)
  • 简化模式 (implicit)
  • 密码模式 (resource owner pwssword credentials)
  • 客户端模式 (client credentials)

Spring Social实现QQ登录

1、实现获取用户信息接口

自定义用户信息实体类:

public class QQUserInfo {

    /**
     * ret : 0
     * msg :
     * nickname : Peter
     * figureurl : http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/30
     * figureurl_1 : http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/50
     * figureurl_2 : http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/100
     * figureurl_qq_1 : http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/40
     * figureurl_qq_2 : http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/100
     * gender : 男
     * is_yellow_vip : 1
     * vip : 1
     * yellow_vip_level : 7
     * level : 7
     * is_yellow_year_vip : 1
     */

    private int ret;
    private String msg;
    private String nickname;
    private String figureurl;
    private String figureurl_1;
    private String figureurl_2;
    private String figureurl_qq_1;
    private String figureurl_qq_2;
    private String gender;
    private String is_yellow_vip;
    private String vip;
    private String yellow_vip_level;
    private String level;
    private String is_yellow_year_vip;

    public int getRet() {
        return ret;
    }

    public void setRet(int ret) {
        this.ret = ret;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getFigureurl() {
        return figureurl;
    }

    public void setFigureurl(String figureurl) {
        this.figureurl = figureurl;
    }

    public String getFigureurl_1() {
        return figureurl_1;
    }

    public void setFigureurl_1(String figureurl_1) {
        this.figureurl_1 = figureurl_1;
    }

    public String getFigureurl_2() {
        return figureurl_2;
    }

    public void setFigureurl_2(String figureurl_2) {
        this.figureurl_2 = figureurl_2;
    }

    public String getFigureurl_qq_1() {
        return figureurl_qq_1;
    }

    public void setFigureurl_qq_1(String figureurl_qq_1) {
        this.figureurl_qq_1 = figureurl_qq_1;
    }

    public String getFigureurl_qq_2() {
        return figureurl_qq_2;
    }

    public void setFigureurl_qq_2(String figureurl_qq_2) {
        this.figureurl_qq_2 = figureurl_qq_2;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getIs_yellow_vip() {
        return is_yellow_vip;
    }

    public void setIs_yellow_vip(String is_yellow_vip) {
        this.is_yellow_vip = is_yellow_vip;
    }

    public String getVip() {
        return vip;
    }

    public void setVip(String vip) {
        this.vip = vip;
    }

    public String getYellow_vip_level() {
        return yellow_vip_level;
    }

    public void setYellow_vip_level(String yellow_vip_level) {
        this.yellow_vip_level = yellow_vip_level;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getIs_yellow_year_vip() {
        return is_yellow_year_vip;
    }

    public void setIs_yellow_year_vip(String is_yellow_year_vip) {
        this.is_yellow_year_vip = is_yellow_year_vip;
    }
}

自定义一个获取用户信息接口:

public interface QQ {

    QQUserInfo getUserInfo() throws JsonProcessingException;
}

获取用户信息接口实现:

/**
 *  获取用户信息
 */
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ  {
    /**
     *  获取openId的url
     */
    private static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
    /**
     *  获取用户信息的url
     */
    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挂在请求上
        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.substring(result,result.indexOf("\"openid\":"), result.lastIndexOf("}"));
    }

    @Override
    public QQUserInfo getUserInfo() throws JsonProcessingException {
        String url = String.format(URL_GET_USERINFO, appId, openId);
        String result = getRestTemplate().getForObject(url,String.class);

        System.out.println(result);

        return objectMapper.readValue(result, QQUserInfo.class);
    }
    }
2、服务提供商

QQ服务提供商调用获取用户信息接口和获取token服务商接口。

public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {

    private String appId;
    /**
     *  获取Authorization Code 授权码
     */
    private static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize";
    /**
     *  权限自动续期,获取Access Token
     */
    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);
    }
}

3、实现QQAdatapter

public class QQAdapter implements ApiAdapter<QQ> {

    @Override
    public boolean test(QQ qq) {
        return true;
    }

    /**
     *  设置ConnectionValues
     * @param api
     * @param connectionValues
     */
    @Override
    public void setConnectionValues(QQ api, ConnectionValues connectionValues) {
            QQUserInfo userInfo = api.getUserInfo();
            connectionValues.setDisplayName(userInfo.getNickname());
            connectionValues.setImageUrl(userInfo.getFigureurl_qq_1());
            connectionValues.setProfileUrl(null);
            connectionValues.setProviderUserId(userInfo.getOpenId());
    }

    @Override
    public UserProfile fetchUserProfile(QQ qq) {
        return null;
    }

    @Override
    public void updateStatus(QQ qq, String s) {
            // do nothing
    }
}

4、实现QQConnectinFactory

public class QQConnectinFactory extends OAuth2ConnectionFactory<QQ> {


    public QQConnectinFactory(String providerId, String appId, String appSeret) {
        super(providerId, new QQServiceProvider(appId, appSeret), new QQAdapter());
    }
}

5、数据连接配置

/**
 *  spring social 配置
 */
@Configuration
@EnableSocial
public class SocialConfig  extends SocialConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        // 第三个参数对用户信息进行加解密
        JdbcUsersConnectionRepository repository =  new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText());
       // 设置表前缀
        repository.setTablePrefix("imooc_");
        return repository;
    }
}

6、在JdbcUsersConnectionRepository类中找到sql文件,在数据库中创建表。

7、登录验证

在用户密码登录时,实现了UserDetailsService,进行用于密码登录验证。使用SpringSocial实现登录验证需要例外实现SocialUserDetailsService接口的loadUserByUserId方法:

/**
 *  用户认证
 */
@Component
public class MyUserDetailsService implements UserDetailsService, SocialUserDetailsService {
   private static final Logger logger = LoggerFactory.getLogger(MyUserDetailsService.class);

   @Autowired
   private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       logger.info("登录用户名:" + username);
       // 根据用户名查找用户信息
        // 根据查找的用户信息判断用户是否被冻结
        /**
         *  第三个参数是权限集合 ,开发中可以将系统的user类,实现UserDetails进行用户信息验证
         */
        return new User(username,passwordEncoder.encode("123456"),
                true,true,true,true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }

    @Override
    public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
        logger.info("社交登录用户登录id:" + userId);
        // 根据用户名查找用户信息
        // 根据查找的用户信息判断用户是否被冻结
        /**
         *  第三个参数是权限集合 ,开发中可以将系统的user类,实现UserDetails进行用户信息验证
         */
        return (SocialUserDetails) new User(userId,passwordEncoder.encode("123456"),
                true,true,true,true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}
8、配置QQ登录所需的appId,appSecret,ProviderId
9、配置QQConnectionFactory
@Configuration
@ConditionalOnProperty(prefix = "imooc.security.social.qq",name = "app-id")
public class QQAutoConfig extends SocialAutoConfigurerAdapter {


    @Autowired
    private SecurityProperties securityProperties;

    @Override
    protected ConnectionFactory<?> createConnectionFactory() {
        QQProperties qqConfig = securityProperties.getSocial().getQq();
        return new QQConnectinFactory(qqConfig.getProviderId(), qqConfig.getAppId(),qqConfig.getAppSecret());
    }
}

10 、将SpringSocialConfigurer添加到Security的过滤器链上。

实例化bean


    @Bean
    public SpringSocialConfigurer imoocSocialSecurityConfig() {
        return new SpringSocialConfigurer();
    }

通过httSecurity的apply方法进行配置

   .apply(imoocSocialSecurityConfig)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值