文章目录
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)