spring-security-oauth2(十) QQ登陆上

QQ登陆上

依据上篇的社交登陆流程我们来进行开发

spring-security-oauth2(十) QQ登陆上_第1张图片

右半边:服务提供商相关实现(1-6步)

ServiceProvider(AbstractOAuth2ServiceProvider):服务提供商抽象(如微信 qq等)

         OAuth2Operations(OAuth2Template):封装了1-5的步骤(标准流程)

         Api(AbstractOAuth2ApiBinding):对第6步提供了支持(获取用户信息,不同服务提供商个性化实现,用户信息结构不一样)

左半边:第三方应用相关实现(第7步相关)

Connection (OAuth2Connection): 封装前6步完成获取到的用户信息

ConnectionFactory(OAuth2ConnectionFactory):创建Connection 工厂,需要下面2个支持

        ServiceProvider :获取前面6步的用户信息Connection(OAuth2Connection)

        ApiAdapter:OAuth2Connection是固定结构的数据,对第三方api返回的用户数据进行匹配(适配);读取用户信息

UsersConnectionRepository(JdbcUsersConnectionRepository): 业务系统用户和服务提供商用户对应关系,保存在表UserConnection

qq互联平台开发参考如下:

qq互联平台

 服务提供商相关实现

API开发

package com.rui.tiger.auth.core.social.qq.api;

/**
 *  获取qq用户信息接口
 * qq互联平台: http://wiki.connect.qq.com/api%E5%88%97%E8%A1%A8
 * @author CaiRui
 * @date 2019-1-3 8:56
 */
public interface QQApi {

	/**
	 * 获取用户信息
	 * @return
	 * @throws Exception
	 */
	QQUserInfo getUserInfo() ;

}
package com.rui.tiger.auth.core.social.qq.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.social.oauth2.TokenStrategy;
import org.springframework.stereotype.Component;

/**
 * 不同用户信息不一样 不能直接@Component(spring默认单例 线程不安全)
 * @author CaiRui
 * @date 2019-1-3 8:56
 */
@Slf4j
public class QQApiImpl extends AbstractOAuth2ApiBinding implements QQApi {

	//获取openID   http://wiki.connect.qq.com/%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7openid_oauth2-0
	public final static String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
	//获取用户信息 父类会自动携带accessToken http://wiki.connect.qq.com/get_user_info
	public final static String URL_GET_USER_INFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";

	/**
	 * 申请QQ登录成功后,分配给应用的appid
 	 */
	private String appId;//
	/**
	 * 通过输入在上一步获取的Access Token,得到对应用户身份的OpenID,与qq号码已一对应
	 * OpenID是此网站上或应用中唯一对应用户身份的标识,网站或应用可将此ID进行存储,便于用户下次登录时辨识其身份,或将其与用户在网站上或应用中的原有账号进行绑定
	 */
	private String openId;


	public QQApiImpl(String accessToken,String appId ){

		//accessToken 抽象父类参数
		super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);//token请求策略 放在header或参数中 QQ要求放在get请求参数中

		this.appId=appId;

		String openIdUrl=String.format(URL_GET_OPENID,accessToken);
		String result=getRestTemplate().getForObject(openIdUrl,String.class);
		//callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
		log.info("qq openID callback:{}",result);
		//先直接截取 这个我们后面重构
		this.openId = StringUtils.substringBetween("\"openid\":", "}");
	}


	@Override
	public QQUserInfo getUserInfo()  {
		String url=String.format(URL_GET_USER_INFO,appId,openId);
		String userInfoResult=getRestTemplate().getForObject(url,String.class);
		//  QQUserInfo userInfo= JSON.parseObject(userInfoResult,QQUserInfo.class);
		log.info("qq userInfoResult json response:{}",userInfoResult);
		QQUserInfo userInfo= JSON.parseObject(userInfoResult, new TypeReference(){});
		return userInfo;
	}
}
package com.rui.tiger.auth.core.social.qq.api;

import lombok.Data;

/**
 * QQ用户信息封装
 * @author CaiRui
 * @date 2019-1-3 8:56
 */
@Data
public class QQUserInfo {

	/**
	 * 返回码 0 成功
	 * 详见公共返回码说明
	 * http://wiki.connect.qq.com/%E5%85%AC%E5%85%B1%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E
	 */
	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;
}

服务提供商开发

package com.rui.tiger.auth.core.social.qq.connect;

import com.rui.tiger.auth.core.social.qq.api.QQApi;
import com.rui.tiger.auth.core.social.qq.api.QQApiImpl;
import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;
import org.springframework.social.oauth2.OAuth2Template;

/**
 * QQ服务提供商
 * http://wiki.connect.qq.com/%E5%BC%80%E5%8F%91%E6%94%BB%E7%95%A5_server-side
 * @author CaiRui
 * @date 2019-1-3 9:10
 */
public class QQServiceProvider extends AbstractOAuth2ServiceProvider {

	//流程第1步 将用户导向认证服务器
	public static final String URL_AUTHORIZE  = "https://graph.qq.com/oauth2.0/authorize";
	//流程第4步 随带授权码获取用户令牌accessToken
	public static final String URL_ACCESS_TOKEN  = "https://graph.qq.com/oauth2.0/token";

	private String appId;//注册qq互联分配的id

	/**
	 * @param appId 注册qq互联分配的id
	 * @param secret 注册qq互联的分配密码
	 */
	public QQServiceProvider(String appId,String secret) {
		// OAuth2Operations 有一个默认实现类,可以使用这个默认实现类
		super(new OAuth2Template(appId, secret, URL_AUTHORIZE , URL_ACCESS_TOKEN ));
		this.appId=appId;
	}

	@Override
	public QQApi getApi(String accessToken) {
		return new QQApiImpl(accessToken, appId);
	}

}

ok 到这里我们的服务提供商相关实现已经完成 下面我们来开发第三方应用相关实现

第三方应用相关实现

ConnectionFactory连接工厂

package com.rui.tiger.auth.core.social.qq.connect;

import com.rui.tiger.auth.core.social.qq.api.QQApi;
import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import org.springframework.social.oauth2.OAuth2ServiceProvider;

/**
 * qq连接工厂
 * @author CaiRui
 * @date 2019-1-3 9:11
 */
public class QQOAuth2ConnectionFactory extends OAuth2ConnectionFactory {
    /**
     *
     * @param providerId 供应商唯一标识 如qq 也是后面添加social的过滤,过滤器帮我们拦截的url其中的某一段地址
     * @param appId  应用id
     * @param appSecret 应用密码
     */
    public QQOAuth2ConnectionFactory(String providerId, String appId,String appSecret) {
        super(providerId, new QQServiceProvider(appId, appSecret), new QQApiAdapter());
    }

}

 ApiAdapter 用户信息转换

package com.rui.tiger.auth.core.social.qq.connect;

import com.rui.tiger.auth.core.social.qq.api.QQApi;
import com.rui.tiger.auth.core.social.qq.api.QQUserInfo;
import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.ConnectionValues;
import org.springframework.social.connect.UserProfile;

/**
 * 适配器,用于将不同服务提供商的个性化用户信息映射到
 * {@link org.springframework.social.connect.Connection}
 * @author CaiRui
 * @date 2019-1-3 9:10
 */
public class QQApiAdapter implements ApiAdapter {

	/**
	 * 测试是否连通
	 * @param api
	 * @return
	 */
	@Override
	public boolean test(QQApi api) {
		return true;
	}

	@Override
	public void setConnectionValues(QQApi api, ConnectionValues values) {
		QQUserInfo userInfo = api.getUserInfo();
		values.setDisplayName(userInfo.getNickname());//昵称
		values.setImageUrl(userInfo.getFigureurl_qq_1());//图像URL地址
		values.setProfileUrl(null); // 主页地址,像微博一般有主页地址
		// 服务提供商返回的该user的openid 一般来说这个openid是和你的开发账户也就是appid绑定的
		values.setProviderUserId(userInfo.getOpenId());
	}

	/**
	 * 获取标准的用户信息
	 * @param api
	 * @return
	 */
	@Override
	public UserProfile fetchUserProfile(QQApi api) {
		return null;
	}
    //更新微博 qq没有用
	@Override
	public void updateStatus(QQApi api, String message) {

	}
}

 配置串联社交登陆相关组件

package com.rui.tiger.auth.core.social;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.social.UserIdSource;
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 org.springframework.social.security.AuthenticationNameUserIdSource;
import org.springframework.social.security.SpringSocialConfigurer;

import javax.sql.DataSource;

/**
 * 社交配置类
 * @author CaiRui
 * @Date 2019/1/5 11:46
 */
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {

    @Autowired
    private DataSource dataSource;//数据源

    /**
     * 默认配置类  包括了过滤器SocialAuthenticationFilter 添加到security过滤链中
     * @return
     */
    @Bean
    public SpringSocialConfigurer tigerSpringSocialConfigurer(){
        return new SpringSocialConfigurer();
    }

    /**
     * 业务系统用户和服务提供商用户对应关系,保存在表UserConnection
     * JdbcUsersConnectionRepository.sql 中有建表语句
     *      userId 业务系统Id
     *      providerId 服务提供商的Id
     *      providerUserId  同openId
     * Encryptors  加密策略 这里不加密
     * @param connectionFactoryLocator
     * @return
     */
    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        JdbcUsersConnectionRepository jdbcUsersConnectionRepository=new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
       //设定表UserConnection的前缀 表名不可以改变
        //jdbcUsersConnectionRepository.setTablePrefix("tiger_");
        return  jdbcUsersConnectionRepository;
    }

    /**
     * 从认证中获取用户信息
     * @return
     */
    @Override
    public UserIdSource getUserIdSource() {
        return new AuthenticationNameUserIdSource();
    }

    //https://docs.spring.io/spring-social/docs/1.1.x-SNAPSHOT/reference/htmlsingle/#creating-connections-with-connectcontroller
    /*@Bean
    public ConnectController connectController(
            ConnectionFactoryLocator connectionFactoryLocator,
            ConnectionRepository connectionRepository) {
        return new ConnectController(connectionFactoryLocator, connectionRepository);
    }*/

}

UsersConnectionRepository(JdbcUsersConnectionRepository): 业务系统用户和服务提供商用户对应关系,保存在表UserConnection ,这个表的建表语句在这个路径下

spring-security-oauth2(十) QQ登陆上_第2张图片

在我们的应用数据库下执行以下建表语句 

CREATE TABLE UserConnection (userId VARCHAR(255) NOT NULL,
 providerId VARCHAR(255) NOT NULL,
 providerUserId VARCHAR(255),
 rank INT NOT NULL,
 displayName VARCHAR(255),
 profileUrl VARCHAR(512),
 imageUrl VARCHAR(512),
 accessToken VARCHAR(512) NOT NULL,
 secret VARCHAR(512),
 refreshToken VARCHAR(512),
 expireTime BIGINT,
 PRIMARY KEY (userId, providerId, providerUserId));
CREATE UNIQUE INDEX UserConnectionRank ON UserConnection(userId, providerId, rank);

 SpringSocialConfigurer要求我们注入 SocialUserDetailsService,之前我们写在core项目中,这个应该放在demo项目中,我们来改造下

package com.rui.tiger.auth.demo.security;

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;

/**
 * 自定义用户登录实现
 * 这里只要是存在一个自定义的 UserDetailsService ,那么security将会使用该实例进行配置
 *
 * @author CaiRui
 * @date 2018-12-5 8:19
 */
@Component
@Slf4j
public class MyUserDetailServiceImpl implements UserDetailsService, SocialUserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 账户密码登陆验证
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        /*//TODO 后续做成数据库实现(MyBaites-plus实现)先实现流程
		//1.根据用户名去数据库去查询用户信息获取加密后的密码 这里模拟一个加密的数据库密码
		String encryptedPassWord = passwordEncoder.encode("123456");
		log.info("模拟加密后的数据库密码:{}", encryptedPassWord);
		//2.这里可以去验证账户的其它相关信息 默认都通过
		//3.返回认证过的用户信息  授予一个admin的权限
		return new User(username,
				encryptedPassWord,
				true,
				true,
				true,
				true,
				AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));*/
        log.info("登录用户名:{}", username);
        return buildUser(username);
    }

    /**
     * 社交登陆
     *
     * @param userId
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
        log.info("社交登录用户ID:" + userId);
        return buildUser(userId);
    }

    private SocialUserDetails buildUser(String userId) {
        // 根据用户名查找用户信息
        //根据查找到的用户信息判断用户是否被冻结
        String password = passwordEncoder.encode("123456");
        log.info("数据库密码是:" + password);
        return new SocialUser(
                userId,
                password,
                true,
                true,
                true,
                true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin")
        );
    }

}

qq配置相关

package com.rui.tiger.auth.core.properties;

/**
 * qq配置文件
 * @author CaiRui
 * @Date 2019/1/5 12:49
 */
public class QQProperties {

    private String providerId="qq";//供应商id

    private String appId;//应用id

    private String appSecret;//应用密匙

    public String getProviderId() {
        return providerId;
    }

    public void setProviderId(String providerId) {
        this.providerId = providerId;
    }

    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;
    }
}
package com.rui.tiger.auth.core.properties;

/**
 * 社交配置
 * @author CaiRui
 * @Date 2019/1/5 12:49
 */
public class SocialProperties {

    private QQProperties qq=new QQProperties();

    public QQProperties getQq() {
        return qq;
    }

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

基础权限配置添加社交配置

package com.rui.tiger.auth.core.properties;

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

/**
 * 权限配置文件父类(注意这里不用lombok 会读取不到)
 * 这里会有很多权限配置子模块
 *
 * @author CaiRui
 * @date 2018-12-6 8:41
 */

@ConfigurationProperties(value = "tiger.auth", ignoreInvalidFields = true)
public class SecurityProperties {
    /**
     * 浏览器配置类
     */
    private BrowserProperties browser = new BrowserProperties();
    /**
     * 验证码配置类
     */
    private CaptchaProperties captcha = new CaptchaProperties();

    /**
     * 社交配置类
     */
    private SocialProperties social=new SocialProperties();


    public BrowserProperties getBrowser() {
        return browser;
    }

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

    public CaptchaProperties getCaptcha() {
        return captcha;
    }

    public void setCaptcha(CaptchaProperties captcha) {
        this.captcha = captcha;
    }

    public SocialProperties getSocial() {
        return social;
    }

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

下面配置qq社交自动配置类,注意这个类新老版本配置不一样,具体可参见官方文档 https://docs.spring.io/spring-social/docs/1.1.x/

package com.rui.tiger.auth.core.social.qq.config;

import com.rui.tiger.auth.core.properties.QQProperties;
import com.rui.tiger.auth.core.properties.SecurityProperties;
import com.rui.tiger.auth.core.social.qq.connect.QQOAuth2ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
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;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;

/**
 * qq社交自动配置类 (新老版本配置不一样)
 * @author CaiRui
 * @Date 2019/1/5 12:32
 */
@Configuration
//有这个配置才会生效
@ConditionalOnProperty(prefix = "tiger.auth.social.qq", name = "app-id")
public class QQAuthoConfig extends SocialConfigurerAdapter {

    @Autowired
    private SecurityProperties securityProperties;

    /*
      SocialAutoConfigurerAdapter  高版本已经移除
      ConnectionFactory createConnectionFactory() 创建连接工厂
      参见  https://docs.spring.io/spring-social/docs/1.1.x/
   */
    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
        connectionFactoryConfigurer.addConnectionFactory(createConnectionFactory());
    }

    private ConnectionFactory createConnectionFactory() {
        QQProperties qq = securityProperties.getSocial().getQq();
        return new QQOAuth2ConnectionFactory(qq.getProviderId(), qq.getAppId(), qq.getAppSecret());
    }

    // 后补:做到处理注册逻辑的时候发现的一个bug:登录完成后,数据库没有数据,但是再次登录却不用注册了
    // 就怀疑是否是在内存中存储了。结果果然发现这里父类的内存ConnectionRepository覆盖了SocialConfig中配置的jdbcConnectionRepository
    // 这里需要返回null,否则会返回内存的 ConnectionRepository
    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        return null;
    }
}

浏览器配置调整,添加社交登陆配置

package com.rui.tiger.auth.browser.config;

import com.rui.tiger.auth.core.config.AbstractChannelSecurityConfig;
import com.rui.tiger.auth.core.config.CaptchaSecurityConfig;
import com.rui.tiger.auth.core.config.SmsAuthenticationSecurityConfig;
import com.rui.tiger.auth.core.properties.SecurityConstants;
import com.rui.tiger.auth.core.properties.SecurityProperties;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.social.security.SpringSocialConfigurer;

import javax.sql.DataSource;

/**
 * 浏览器security配置类
 *
 * @author CaiRui
 * @date 2018-12-4 8:41
 */
@Configuration
public class BrowserSecurityConfig extends AbstractChannelSecurityConfig {

	@Autowired
	private SecurityProperties securityProperties;
	@Autowired
	private DataSource dataSource;
	@Autowired
	private UserDetailsService userDetailsService;
	@Autowired
	private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;//短信登陆配置
	@Autowired
	private CaptchaSecurityConfig captchaSecurityConfig;//验证码配置
	@Autowired
	private SpringSocialConfigurer tigerSpringSocialConfigurer;

	/**
	 * 密码加密解密
	 *
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	/**
	 * 记住我持久化数据源
	 * JdbcTokenRepositoryImpl  CREATE_TABLE_SQL 建表语句可以先在数据库中执行
	 *
	 * @return
	 */
	@Bean
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
		jdbcTokenRepository.setDataSource(dataSource);
		//第一次会执行CREATE_TABLE_SQL建表语句 后续会报错 可以关掉
		//jdbcTokenRepository.setCreateTableOnStartup(true);
		return jdbcTokenRepository;
	}

	/**
	 * 核心配置
	 * @param http
	 * @throws Exception
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		/**
		 * 表单密码配置
		 */
		applyPasswordAuthenticationConfig(http);

		http
				.apply(captchaSecurityConfig)
					.and()
				.apply(smsAuthenticationSecurityConfig)
					.and()
				.apply(tigerSpringSocialConfigurer)
					.and()
				.rememberMe()
				.tokenRepository(persistentTokenRepository())
				.tokenValiditySeconds(securityProperties.getBrowser().getRemberMeSeconds())
				.userDetailsService(userDetailsService)
					.and()
				.authorizeRequests()
				.antMatchers(
						SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,//权限认证
						SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,//手机
						securityProperties.getBrowser().getLoginPage(),//登录页面
						SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*")// /captcha/* 验证码放行
				.permitAll()
				.anyRequest()
				.authenticated()
					.and()
				.csrf().disable();

	}

}

ok 在我们的配置文件中添加在qq互联申请的测试账号,我是直接用下面这儿已经申请好的,非常感谢

spring-security-oauth2(十) QQ登陆上_第3张图片

http://www.spring4all.com/article/424

#数据源
spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://my.yunout.com:3306/tiger_study?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root
    # 配置Druid连接池
    type: com.alibaba.druid.pool.DruidDataSource
  session:
    store-type: none

# Tomcat
server:
  port: 8070
  connection-timeout: 5000ms

#自定义权限配置
tiger:
  auth:
     browser:
       #loginPage: /demo-login.html # 这里可以配置成自己的非标准登录界面
        loginType: JSON
     imageCaptcha:
        interceptImageUrl: /user/*,/pay/confirm # 这些路径验证码也要拦截校验
     social:
        qq:
          app-id: 101386962
          app-secret: 2a0f820407df400b84a854d054be8b6a
          # http://www.ictgu.cn/login/qq

标准登陆界面添加qq登陆



    
    登录


标准登录页面

表单登录

用户名:
密码:
图形验证码:
记住我

短信登录

手机号:
短信验证码: 发送验证码

社交登录

我们来测试一下,打开登陆界面,点击qq登陆

spring-security-oauth2(十) QQ登陆上_第4张图片

这是怎么回事呢?下章我们来分析并解决。

你可能感兴趣的:(spring-security-oauth2(十) QQ登陆上)