SpringSecurity学习总结-4 第五章 使用Spring Social开发第三方登录(1)

结局:总结+分享

看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。

开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱

  • Java互联网工程师面试1000题

image.png

而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。

  • 程序员代码面试指南–IT名企算法与数据结构题目最优解

image.png

  • 其余像设计模式,建议可以看看下面这4份PDF(已经整理)

image.png

  • 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。

image.png

以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

private String appId;

//1、第三方应用将用户带到服务提供商的URL获取授权码
private static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize";

//2、第三方应用拿着授权码获取令牌的URL
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));
}

/**
 * 获取QQ的实现类
 * @param accessToken
 * @return
 */
@Override
public QQ getApi(String accessToken) {
    return new QQImpl(accessToken,appId);
}

}


### 3.3新建 ApiAdapter的实现类QQAdapter



package security.core.social.qq.connect;

import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.ConnectionValues;
import org.springframework.social.connect.UserProfile;
import security.core.social.qq.api.QQ;
import security.core.social.qq.api.QQUserInfo;

/**

  • ApiAdapter里的泛型是要适配接口,这里是QQ
    */
    public class QQAdapter implements ApiAdapter {

    /**

    • 测试QQ接口是否可用
    • @param qq
    • @return
      */
      @Override
      public boolean test(QQ qq) {
      return true;
      }

    /**

    • 数据适配:将QQ接口获取到的数据转换成Connection里标准数据

    • @param qq

    • @param connectionValues
      */
      @Override
      public void setConnectionValues(QQ qq, ConnectionValues connectionValues) {
      QQUserInfo userInfo = qq.getUserInfo();

      connectionValues.setDisplayName(userInfo.getNickname());
      connectionValues.setImageUrl(userInfo.getFigureurl_qq_1());
      //个人主页:QQ是没有个人主页的,设为null
      connectionValues.setProfileUrl(null);
      connectionValues.setProviderUserId(userInfo.getOpenId());
      }

    /**

    • 获取用户主页信息,暂时用不到
    • @param qq
    • @return
      */
      @Override
      public UserProfile fetchUserProfile(QQ qq) {
      return null;
      }

    /**

    • 更新QQ信息(用不到,QQ无此功能)
    • @param qq
    • @param s
      */
      @Override
      public void updateStatus(QQ qq, String s) {

    }
    }


### 3.3 新建ConnectionFacotory并继承OAuth2Connection



package security.core.social.qq.connect;

import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import security.core.social.qq.api.QQ;

/**

  • QQConnectionFactory继承OAuth2ConnectionFactory的泛型是要适配的接口,作用是用来生成Connection
    */
    public class QQConnectionFactory extends OAuth2ConnectionFactory {

    /**

    • QQConnectionFactory的构造函数
    • @param providerId
    • @param appId
    • @param appScret
      /
      public QQConnectionFactory(String providerId,String appId,String appScret) {
      /
      *
      • providerId:服务提供商的唯一标识
      • serviceProvider:QQServiceProvider
      • apiAdapter:QQAdapter
        */
        super(providerId, new QQServiceProvider(appId,appScret), new QQAdapter());
        }
        }

### 3.4 新建一个配置类SocialConfig,使用springsocial的配置将从第三方获取到的用户数据存储到数据库中



package security.core.social;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.social.config.annotation.EnableSocial;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;

import javax.sql.DataSource;

/**

  • SocialConfig:用于将从第三方获取到的用户数据存储到数据库中

  • EnableSocial:将项目的social特性开启起来
    */
    @Configuration
    @EnableSocial
    public class SocialConfig extends SocialConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    /**

    • JdbcUsersConnectionRepository构造函数的三个参数的作用:
    • dataSource:用于连接数据库
    • connectionFactoryLocator:用于查找具体使用的ConnectionFactoryLocator
    • textEncryptor:用于加解密数据
    • @param connectionFactoryLocator
    • @return
      */
      @Override
      public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
      //Encryptors.noOpText():是不做任何加解密
      return new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText());
      }
      }

### 3.5 将 JdbcUsersConnectionRepository使用的数据表的建表语句拿到数据库里执行建表,建表语句在**JdbcUsersConnectionRepository**类的旁边


![](https://img-blog.csdnimg.cn/20200517191523578.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01yX1hpTXU=,size_16,color_FFFFFF,t_70)


### 3.6给新建的数据表加入前缀



/**
 * JdbcUsersConnectionRepository构造函数的三个参数的作用:
 * dataSource:用于连接数据库
 * connectionFactoryLocator:用于查找具体使用的ConnectionFactoryLocator
 * textEncryptor:用于加解密数据
 * @param connectionFactoryLocator
 * @return
 */
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
    //Encryptors.noOpText():是不做任何加解密
    JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText());
    //setTablePrefix:设置表的前缀
    repository.setTablePrefix("u_");
    return repository;
}

完整代码:



package security.core.social;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.social.config.annotation.EnableSocial;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;

import javax.sql.DataSource;

/**

  • SocialConfig:用于将从第三方获取到的用户数据存储到数据库中

  • EnableSocial:将项目的social特性开启起来
    */
    @Configuration
    @EnableSocial
    public class SocialConfig extends SocialConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    /**

    • JdbcUsersConnectionRepository构造函数的三个参数的作用:
    • dataSource:用于连接数据库
    • connectionFactoryLocator:用于查找具体使用的ConnectionFactoryLocator
    • textEncryptor:用于加解密数据
    • @param connectionFactoryLocator
    • @return
      */
      @Override
      public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
      //Encryptors.noOpText():是不做任何加解密
      JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText());
      //setTablePrefix:设置表的前缀
      repository.setTablePrefix(“u_”);
      return repository;
      }
      }

### 3.7 使用 userId获取用户信息


修改**MyUserDetailsService**,加入实现**SocialUserDetailsService**,用于通过userId来获取用户信息



package security.browser.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.social.security.SocialUser;
import org.springframework.social.security.SocialUserDetails;
import org.springframework.social.security.SocialUserDetailsService;
import org.springframework.stereotype.Component;

/**

  • SocialUserDetailsService:是social的获取用户信息的接口类
    */
    @Component
    @Slf4j
    public class MyUserDetailsService implements UserDetailsService, SocialUserDetailsService {

    @Autowired
    private PasswordEncoder myPasswordEncoder;
    /**

    • 可以@AutowiredDao层接口从而实现根据用户名去查找用户信息

    • @param username

    • @return

    • @throws UsernameNotFoundException
      */
      @Override
      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      log.info("用户名: "+username);

      String password = myPasswordEncoder.encode(“123456”);
      log.info(“密码:”+password);

      //这个User类实现了UserDetails
      //密码应该是数据库查询出的密码
      //authorities:用户权限的集合,即用来给用户授权
      User user = new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList(“admin”));

      return user;
      }

    /**

    • 通过userId获取用户信息

    • @param userId

    • @return

    • @throws UsernameNotFoundException
      */
      @Override
      public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
      log.info("用户Id: "+userId);

      String password = myPasswordEncoder.encode(“123456”);
      log.info(“密码:”+password);

      //这个User类实现了UserDetails
      //密码应该是数据库查询出的密码
      //authorities:用户权限的集合,即用来给用户授权
      return new SocialUser(userId,password, AuthorityUtils.commaSeparatedStringToAuthorityList(“admin”));
      }
      }


#### 3.8新建QQProperties的相关配置类



package security.core.properties;

/**

  • QQ登录的相关配置
    */
    public class QQProperties{

    /**

    • 从服务提供商处申请的本项目的appId
      */
      private String appId;

    /**

    • 从服务提供商处申请的本项目的appSecret
      */
      private String appSecret;

    /**

    • 服务提供商的标识
      */
      private String provideId = “qq”;

    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 getProvideId() {
    return provideId;
    }

    public void setProvideId(String provideId) {
    this.provideId = provideId;
    }
    }


### 3.9 新建SocialProperties相关的配置类



package security.core.properties;

/**

  • 社交相关的配置类
    */
    public class SocialProperties {

    private QQProperties qq = new QQProperties();

    public QQProperties getQqProperties() {
    return qq;
    }

    public void setQqProperties(QQProperties qq) {
    this.qq = qq;
    }
    }


### 3.10 在SecurityProperties中加入SocialProperties配置



/**
 * 社交相关的配置
 */
private SocialProperties social = new SocialProperties();


public SocialProperties getSocial() {
    return social;
}

public void setSocial(SocialProperties social) {
    this.social = social;
}

完整代码:



package security.core.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(“classpath:application-browser.properties”)
//@PropertySource(value= {“classpath:application-demo.properties”,“classpath:application-browser.properties”})
@ConfigurationProperties(“imooc.security”)
public class SecurityProperties {

/**
 * 浏览器相关的配置
 */
private BrowserProperties browser = new BrowserProperties();

/**
 * 验证码相关的配置
 */
private ValidateCodeProperties code = new ValidateCodeProperties();

/**
 * 社交相关的配置
 */
private SocialProperties social = new SocialProperties();

public BrowserProperties getBrowser() {
    return browser;
}

public void setBrowser(BrowserProperties browser) {
    this.browser = browser;
}

public ValidateCodeProperties getCode() {
    return code;
}

public void setCode(ValidateCodeProperties code) {
    this.code = code;
}

public SocialProperties getSocial() {
    return social;
}

public void setSocial(SocialProperties social) {
    this.social = social;
}

}


### 3.11 新建SocialAutoConfigurerAdapter,因为使用的SpringSocial中没有SocialAutoConfigurerAdapter,所以新建一个



package security.core.social.qq.config;

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;

/**

  • 因为使用的SpringSocial中没有SocialAutoConfigurerAdapter,所以新建一个
    */
    public abstract class SocialAutoConfigurerAdapter extends SocialConfigurerAdapter {
    public SocialAutoConfigurerAdapter() {
    }
    public void addConnectionFactories(ConnectionFactoryConfigurer configurer, Environment environment) {
    configurer.addConnectionFactory(this.createConnectionFactory());
    }
    protected abstract ConnectionFactory<?> createConnectionFactory();
    }

### 3.12 新建 QQAutoConfig,用于将QQ的配置注入到ConnectionFactory的构造参数中



package security.core.social.qq.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.social.connect.ConnectionFactory;
import security.core.properties.QQProperties;
import security.core.properties.SecurityProperties;
import security.core.social.qq.connect.QQConnectionFactory;

/**

  • 用于将QQ的配置注入到ConnectionFactory的构造参数中
  • ConditionalOnProperty:作用是只有当系统中配置了appId时QQAutoConfig配置类才会生效
    */
    @Configuration
    @ConditionalOnProperty(value = “imooc.security.social.qq”,name = “appId”)
    public class QQAutoConfig extends SocialAutoConfigurerAdapter{

@Autowired
private SecurityProperties securityProperties;

/**
 * 在创建ConnectionFactory时将参数注入进去
 * @return
 */
@Override
protected ConnectionFactory<?> createConnectionFactory() {
    QQProperties qqconfig = securityProperties.getSocial().getQqProperties();
    return new QQConnectionFactory(qqconfig.getProvideId(),qqconfig.getAppId(),qqconfig.getAppSecret());
}

}


### 3.13 在application-browser.properties属性文件中加入



imooc.security.social.qq.appId =
imooc.security.social.qq.appSecret =


### 3.14 在过滤器链中加入SocialAuthenticationFilter过滤器


#### 3.14.1、将SpringSocial的Bean写在**SocialConfig**中



/**
 * SpringSocial的配置,这个Bean也可以挪到 BrowserSecurityConfig里
 * @return
 */
@Bean
public SpringSocialConfigurer imoocSecuritySocialConfig(){
    return new SpringSocialConfigurer();
}

#### 3.14.2、在SpringSecurity的过滤器链中加入SpringSocial的配置



//加入SpringSocialConfigurer的配置
@Autowired
private SpringSocialConfigurer socialConfigurer;

/************************************************/
.apply(socialConfigurer).and()//将SpringSocial的过滤器配置加入其中



BrowserSecurityConfig的完整代码:




package security.browser.config;

import imooc.security.msn.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.social.security.SpringSocialConfigurer;
import security.core.authentication.handle.MsnCodeAuthenticationFailureHandler;
import security.core.authentication.handle.MsnCodeAuthenticationSuccessHandler;
import security.core.authentication.mobile.MsnCodeAuthenticationSecurityConfig;
import security.core.properties.SecurityProperties;
import security.core.validateCode.ImageCodeGenerator;
import security.core.validateCode.ValidateCodeFilter;
import security.core.validateCode.ValidateCodeGenerator;

import javax.sql.DataSource;

/**

  • WebSecurityConfigurerAdapter是SpringSecurity提供的安全适配器类
    */
    @Configuration
    public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

    /**

    • 读取配置信息
      */
      @Autowired
      private SecurityProperties securityProperties;

    /**

    • 自定义的登陆成功的处理器
      */
      @Autowired
      private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;

    /**

    • 自定义的登陆失败的处理器
      */
      @Autowired
      private AuthenticationFailureHandler imoocAuthenticationFailureHandler;

    @Autowired
    private MsnCodeAuthenticationSecurityConfig msnCodeAuthenticationSecurityConfig;

    //加入SpringSocialConfigurer的配置
    @Autowired
    private SpringSocialConfigurer socialConfigurer;

    @Override
    public void configure(WebSecurity web) throws Exception {
    //解决静态资源被SpringSecurity拦截的问题
    // “/static/“的意思是任何以 static地址开头的资源都能被访问
    web.ignoring().antMatchers(”/static/
    ”,“/jquery/“,”/layui/”);
    }
    /**
    *

    • @param http

    • @throws Exception
      */
      @Override
      protected void configure(HttpSecurity http) throws Exception {

      //加入图片验证码的前置校验过滤器
      ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
      validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);
      //设置可配置的拦截url
      validateCodeFilter.setSecurityProperties(securityProperties);
      validateCodeFilter.afterPropertiesSet();

      http.apply(msnCodeAuthenticationSecurityConfig).and()
      .apply(socialConfigurer).and()//将SpringSocial的过滤器配置加入其中
      .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
      .formLogin()//开启表单登录(即对表单登录进行身份认证)
      // http.formLogin()//开启表单登录(即对表单登录进行身份认证)
      .loginPage(“/authentication/require”)//指定登录页面
      .loginProcessingUrl(“/authentication/form”)//让UsernamePasswordAuthenticationFilter能够处理提交的登录请求
      .successHandler(imoocAuthenticationSuccessHandler)
      .failureHandler(imoocAuthenticationFailureHandler)
      // .and()
      // .rememberMe()
      // .tokenRepository(persistentTokenRepository())
      // .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSecond())
      // .userDetailsService(userDetailsService)
      // http.httpBasic()//开启SpringSecurity原生的表单登录
      .and()
      .authorizeRequests()//对请求进行授权(即登录后需要授权)
      .antMatchers(“/authentication/require”,“/authentication/mobile”,
      securityProperties.getBrowser().getLoginPage(),
      “/code/*”).permitAll()//允许signIn.html请求进来,不进行拦截
      .anyRequest()//对任何请求
      .authenticated()//开启认证
      .and()
      .csrf() //跨域请求伪造
      .disable();//关闭

// .anyRequest()
// .authenticated();
// 上面两个方法的意思:对任何请求都需要认证

}

@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();

Ending

Tip:由于文章篇幅有限制,下面还有20个关于MySQL的问题,我都复盘整理成一份pdf文档了,后面的内容我就把剩下的问题的目录展示给大家看一下

如果觉得有帮助不妨【转发+点赞+关注】支持我,后续会为大家带来更多的技术类文章以及学习类文章!(阿里对MySQL底层实现以及索引实现问的很多)

吃透后这份pdf,你同样可以跟面试官侃侃而谈MySQL。其实像阿里p7岗位的需求也没那么难(但也不简单),扎实的Java基础+无短板知识面+对某几个开源技术有深度学习+阅读过源码+算法刷题,这一套下来p7岗差不多没什么问题,还是希望大家都能拿到高薪offer吧。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

     上面两个方法的意思:对任何请求都需要认证

}

@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();

Ending

Tip:由于文章篇幅有限制,下面还有20个关于MySQL的问题,我都复盘整理成一份pdf文档了,后面的内容我就把剩下的问题的目录展示给大家看一下

如果觉得有帮助不妨【转发+点赞+关注】支持我,后续会为大家带来更多的技术类文章以及学习类文章!(阿里对MySQL底层实现以及索引实现问的很多)

[外链图片转存中…(img-HJKgM3ez-1715470491743)]

[外链图片转存中…(img-BWouMFBQ-1715470491743)]

吃透后这份pdf,你同样可以跟面试官侃侃而谈MySQL。其实像阿里p7岗位的需求也没那么难(但也不简单),扎实的Java基础+无短板知识面+对某几个开源技术有深度学习+阅读过源码+算法刷题,这一套下来p7岗差不多没什么问题,还是希望大家都能拿到高薪offer吧。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值