• 博客之QQ登录功能(一)


    流程图
    在这里插入图片描述
    在这里插入图片描述

    上图spring social 封装了1-8步需要的工作
    在这里插入图片描述

    1、新建包和书写配置文件

    在这里插入图片描述
    在这里插入图片描述

    public class QQProperties {
    
    	//App唯一标 识
    	private String appId = "100550231";
    	private String appSecret = "69b6ab57b22f3c2fe6a6149274e3295e";
    	
    	//QQ供应商
    	private String providerId = "callback.do";
    	//拦截器拦截的请求
    	private String filterProcessesUrl = "/qqLogin";
    	
    	//get set 方法
    	//...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    @ConfigurationProperties(prefix = "blog.security")
    public class BlogSecurityProperties {
    	
    	private QQProperties qqProperties = new QQProperties();
    
    	//get set...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    @Configuration
    //让我们的配置生效
    @EnableConfigurationProperties (BlogSecurityProperties.class)
    public class BlogSecurityConfig {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、获取QQ用户信息

    package com.zzz.blog.social.qq.api;
    
    public interface QQ {
    
    	//返回一个QQ的用户信息
    	QQUserInfo getUserInfo();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    package com.zzz.blog.social.qq.api;
    
    import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
    
    public class QQImpl extends AbstractOAuth2ApiBinding implements QQ{
    
    	private static final String URL_GET_USERINFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";
    	
    	//外界赋值
    	private String appId;
    	//用户的唯一 标识,url
    	private String openId;
    	
    	private ObjectMapper objectMapper = new ObjectMapper();
    
    	@Override
    	public QQUserInfo getUserInfo() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    package com.zzz.blog.social.qq.api;
    
    public class QQUserInfo {
    
    	private String is_lost;
    	private String province;
    	private String city;
    	private String year;
    	private String constellation;
    	
    	private String 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 figureurl_qq;
    	private String figureurl_type;
    	private String gender_type;
    	
    	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;
    	
    	private String openId;
    	
    	//get/set...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    修改代码

    	//获取用户信息
    	@Override
    	public QQUserInfo getUserInfo() {
    		
    		//拼接参数
    		String url = String.format(URL_GET_USERINFO, appId, openId);
    		//发送请求
    		String result = getRestTemplate().getForObject(url, String.class);
    		
    		//处理返回值
    		QQUserInfo userInfo = null;
    
    			try {
    				userInfo = objectMapper.readValue(result, QQUserInfo.class);
    				userInfo. setOpenId(openId);
    			} catch (JsonProcessingException e) {
    				throw new RuntimeException(" 获取用户信息失败!");
    			}
    
    		return userInfo;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3、如何获得OpenId以及AppId

    在这里插入图片描述

    public class QQImpl extends AbstractOAuth2ApiBinding implements QQ{
    	
    	private static final String URL_GET_USERINFO = "https://graph.qq.com/user/get_user_info?pauth_consumer_key=%s&openid=%s";
    	
    	private static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
    	
    	//外界赋值
    	private String appId;
    	//用户的唯一 标识,url
    	private String openId;
    
    	private ObjectMapper objectMapper = new ObjectMapper();
    
    	public QQImpl(String accessToken, String appId) {
    		//自动拼接一个参数
    		super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER) ;
    		//赋值appid 
    		this.appId = appId;
    		//赋值openid
    		//通过url获得openid
    		//拼接参数
    		String url = String.format(URL_GET_OPENID, accessToken);
    		
    		//发送请求
    		String result = getRestTemplate().getForObject(url, String.class);
    		//处理返回值
    		//callback( {"client_ id":"100550231", ”openid":"CDF1A28F8698E326D173DE17437FB098"} );
    		result = StringUtils.replace(result, "callback( ","");
    		result = StringUtils.replace(result, " );","");
    		//{"client_ id": "100550231","openid": "CDF1A28F8698E326D173DE17437FB098"}
    		OpenId id = null;
    		
    		try {
    			id = objectMapper.readValue(result, OpenId.class);
    		} catch (JsonProcessingException e) {
    			throw new RuntimeException( "获取OpenId失败! ! ");
    		}
    		
    		//赋值openid
    		this.openId = id.getOpenid();
    	}
    	
    	//获取用户信息
    	@Override
    	public QQUserInfo getUserInfo() {
    		...
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    package com.zzz.blog.social.qq.api;
    
    public class OpenId {
    
    	private String client_id;
    	private String openid;
    
    	//get/set
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4、完成QQOAuth2Template

    package com.zzz.blog.social.qq.template;
    
    import ...
    
    public class QQOAuth2Template extends OAuth2Template{
    
    	public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
    		super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
    		//使clientId、clientSecret可以拼接到一起
    		setUseParametersForClientAuthentication(true);
    	}
    
    	//添加text/html
    	@Override
    	protected RestTemplate createRestTemplate() {
    		RestTemplate template = super.createRestTemplate();
    		template.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
    		
    		return template;
    	}
    
    	//把请求的格式按照qq的标准,做了一些自定义信息 自己处理请求 按&分割字符,分割后如下
    	//access_token=FE04***** *****************CCE2  items[0]
    	//expires_in=7776000 item[0]
    	//refresh_token=88E4********* **************BE14 item[1]
    	@Override
    	protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
    		
    		String responseStr = getRestTemplate().postForObject(accessTokenUrl, parameters, String.class);
    		
    		//StringUtils.split只切割了一次,坑
    		String[] items = StringUtils.split(responseStr, "&");
    		String[] item = StringUtils.split(items[1], "&");
    		
    		String access_token =StringUtils.replace(items[0], "access_token=", "");
    		Long expires_in = new Long(StringUtils.replace(item[0], "expires_in=", ""));
    		String refresh_token = StringUtils.replace(item[1], "refresh_token=", "");
    		
    		
    		return new AccessGrant(access_token, null, refresh_token, expires_in);
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    5、完成QQAdapter与ServiceProvider

    package com.zzz.blog.social.qq.connection;
    
    import ...
    
    public class QQAdapter implements ApiAdapter<QQ>{
    
    	@Override
    	public boolean test(QQ api) {
    		// 始终为true
    		return true;
    	}
    
    	@Override
    	public void setConnectionValues(QQ api, ConnectionValues values) {
    		//获取userinfo
    		QQUserInfo userInfo = api. getUserInfo();
    		
    		//获取用户名称
    		values.setDisplayName(userInfo.getNickname());
    		//获取头像
    		values.setImageUrl(userInfo.getFigureurl_qq_1());
    		//获取个人主页
    		values.setProfileUrl(null);
    		//openid,用户在服务商中的唯一标识
    		values.setProviderUserId(userInfo.getOpenId());
    		
    	}
    
    	@Override
    	public UserProfile fetchUserProfile(QQ api) {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void updateStatus(QQ api, String message) {
    		// TODO Auto-generated method stub
    		
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    package com.zzz.blog.social.qq.connection;
    
    import ...
    
    public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ>{
    
    	//将用户导向认证服务器中的ur1地址,用户在该地址上进行授权
    	private static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize";
    	//在获取令牌的时候,需要访问的url
    	private static final String URL_ACCESSTOEKN = "https://graph.qq.com/oauth2.0/token";
    	
    	private String appId;
    	
    	//1-6
    	public QQServiceProvider(String appId,String appSecret) {
    		super(new QQOAuth2Template(appId, appSecret, URL_AUTHORIZE, URL_ACCESSTOEKN));
    		this.appId = appId;
    	}
    
    	//7-8
    	@Override
    	public QQ getApi(String accessToken) {
    		// TODO Auto-generated method stub
    		return new QQImpl(accessToken, appId);
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    6、完成QQConfig与ConnectionFactory

    package com.zzz.blog.social.qq.connection;
    
    import ...
    
    public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ>{
    
    	public QQConnectionFactory(String providerId, String appId,String appSecret) {
    		super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter());
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    package com.zzz.blog.social.qq.config;
    
    import ...
    
    @Configuration
    @EnableSocial
    @Order(2)
    public class QQConfig extends SocialConfigurerAdapter{
    
    	@Autowired
    	private BlogSecurityProperties blogSecurityProperties;
    
    	//添加qq创建connection的工厂
    	@Override
    	public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer,
    			Environment environment) {
    		
    		QQProperties qqConfig = blogSecurityProperties.getQqProperties();
    
    		QQConnectionFactory qqConnectionFactory = new QQConnectionFactory(qqConfig.getProviderId(), qqConfig.getAppId(), qqConfig.getAppSecret());
    		
    		connectionFactoryConfigurer.addConnectionFactory(qqConnectionFactory);
    	}
    	
    	//获取登陆人
    	@Override
    	public UserIdSource getUserIdSource() {
    		return new AuthenticationNameUserIdSource();
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    7、创建表以及创建操作表的类JdbcUsersConnectionRepository

    package com.zzz.blog.social.qq.config;
    
    import ...
    
    @Configuration
    @EnableSocial
    @Order(1)
    public class SocialConfig extends SocialConfigurerAdapter{
    	
    	@Autowired
    	private DataSource dataSource;
    	
    	//登录之后,直接将QQ的数据保存在数据库
    	@Override
    	public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
    		JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
    		return repository;
    	}	
    	
    	//改变拦截的请求
    	
    	//在注册的过程中,拿到了这个SpringSocial中的信息
    	//业务完成之后,把用户的id传给了SpringSocial
    	
    	//打开ConnectController
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    找到socailJDBC表格式,在数据库中执行sql
    在这里插入图片描述

    8、改变拦截的请求

    package com.zzz.blog.social.qq.config;
    
    import ...
    
    public class ZZZSpringSocialConfigurer extends SpringSocialConfigurer{
    
    	private String filterProcessesUrl;
    
    	public ZZZSpringSocialConfigurer(String filterProcessesUrl) {
    		this.filterProcessesUrl = filterProcessesUrl;
    	}
    
    	
    	//将默认的拦截改为qqLogin
    	@Override
    	protected <T> T postProcess(T object) {
    		//获得filter
    		SocialAuthenticationFilter filter = (SocialAuthenticationFilter)super.postProcess(object);
    		//设置字段
    		filter.setFilterProcessesUrl(filterProcessesUrl);
    		return (T) filter;
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    	//改变拦截的请求 /auth -> /qqLogin
    	@Bean
    	public SpringSocialConfigurer zzzSocialSecurityConfig() {
    		String filterProcessesUrl = blogSecurityProperties.getQqProperties().getFilterProcessesUrl();
    		ZZZSpringSocialConfigurer zzzSpringSocialConfigurer = new ZZZSpringSocialConfigurer(filterProcessesUrl);
    		return zzzSpringSocialConfigurer;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    9、将Social中的配置生效到SpringSecurity中

    在SocialConfig类中添加代码

    	//在注册的过程中,拿到了这个SpringSocial中的信息
    	//业务完成之后,把用户的id传给了SpringSocial
    	@Bean
    	public ProviderSignInUtils providerSignInUtils() {
    		return new ProviderSignInUtils(connectionFactoryLocator, getUsersConnectionRepository(connectionFactoryLocator));
    	}
    	
    	//打开ConnectController
    	@Bean
    	public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator,ConnectionRepository connectionRepository) {
    		return new ConnectController(connectionFactoryLocator, connectionRepository);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    添加apply配置socialconfig

    package com.zzz.blog.config;
    
    import ...
    
    //安全配置类
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter{
    
    	//SpringSecurity加密方法返回值
    	@Bean
    	public PasswordEncoder passwordEncoder() {
    		return new BCryptPasswordEncoder();
    	}
    	
    	@Autowired
    	private SpringSocialConfigurer zzzSocialSecurityConfig;
    	
    	//做拦截
    	@Override
    	protected void configure(HttpSecurity http) throws Exception {
    		// 请求授权
    		http.formLogin().and().authorizeRequests()
    		//授权放行
    		.antMatchers("/*.html").permitAll()
    		//所有请求
    		.anyRequest()
    		//都需要身份认证
    		.authenticated().and()
    		//43、使用Layer打开select-mood子页面并配置SpringSecurity允许Iframe嵌入页面 
    		.headers().frameOptions().disable().and()
    		//跨站请求伪造的防护
    		.csrf().disable()
    		//添加我们所写的spring social配置
    		.apply(zzzSocialSecurityConfig);
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    10、创建Visitor实体并实现SocialUserDetailsService接口查找Visitor

    package com.zzz.blog.domain;
    
    import ...
    
    @Entity
    public class Visitor {
    
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    	private String username;
    	private String password;
    	private String image;
    	
    	protected Visitor() {
    		
    	}
    
    	public Visitor(Long id, String username, String password, String image) {
    		super();
    		this.id = id;
    		this.username = username;
    		this.password = password;
    		this.image = image;
    	}
    
    	//get/set
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    package com.zzz.blog.repository;
    
    import ...
    
    public interface VisitorRepository extends CrudRepository<Visitor, Long>{
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    @Component
    public class SocialVisitorServiceImpl implements SocialUserDetailsService{
    
    	@Autowired
    	private VisitorRepository visitorRepository;
    	
    	@Autowired
    	private PasswordEncoder passwordEncoder;
    	
    	@Override
    	public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
    
    		//根据userId查找访客
    		Optional<Visitor> optional = visitorRepository.findById(new Long(userId));
    		Visitor visitor = optional.get();
    		if (visitor == null) {
    			throw new UsernameNotFoundException(userId);
    		}
    		
    		return new SocialUser(visitor.getUsername(), passwordEncoder.encode(visitor.getPassword()), true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("VISITOR"));
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    11、实现ConnectionSignUp接口添加Visitor

    package com.zzz.blog.social.qq.signup;
    
    import ...
    
    @Component
    public class DemoConnectionSignUp implements ConnectionSignUp{
    
    	@Autowired
    	private VisitorService visitorService;
    	
    	//根据社交用户的信息,创建一个Visitor并返回唯一标识
    	@Override
    	public String execute(Connection<?> connection) {
    		
    		Visitor visitor = new Visitor(null, connection.getDisplayName(), "123456", connection.getImageUrl());
    		
    		visitor = visitorService.saveVisitory(visitor);
    		
    		return visitor.getId().toString();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    package com.zzz.blog.service;
    
    import ...
    
    @Service
    public interface VisitorService {
    
    	Visitor saveVisitory(Visitor visitor);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    package com.zzz.blog.service;
    
    import ...
    
    @Component
    public class VisitorServiceImpl implements VisitorService{
    
    	@Autowired
    	private VisitorRepository visitorRepository;
    	
    	@Override
    	public Visitor saveVisitory(Visitor visitor) {
    		return visitorRepository.save(visitor);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    SocialConfig添加setConnectionSignUp执行方法

    	@Autowired
    	private ConnectionSignUp connectionSignUp;
    	
    	//登录之后,直接将QQ或微信的数据保存在数据库,保存在usersconnection表和visitor表中
    	//上层使用jdbc,先通过providerUserId查询是否存在usersconnection表,存在则返回userId,去SocialUserDetailsService登录用户名,密码随意不校验
    	//usersconnection表不存在,先插入visitor表,根据user的id再插入usersconnection表,返回userId,去SocialUserDetailsService登录用户名,密码随意不校验
    	@Override
    	public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
    		JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
    		repository.setConnectionSignUp(connectionSignUp);
    		return repository;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    12、测试QQ登录

    application.properties添加代码如下(修改端口):

    server.port=80
    
    • 1

    login.html修改超链接代码如下:

    						<a href="/qqLogin/callback.do" class="login100-social-item bg2">
    							<i class="fa fa-qq">i>
    						a>
    
    • 1
    • 2
    • 3

    修改C:\Windows\System32\drivers\etc\hosts文件

    127.0.0.1        www.pinzhi365.com
    
    • 1

    这是别人提供的测试地址。我们也可以到QQ互联官网https://connect.qq.com/上注册用户,创建应用。
    在这里插入图片描述
    其中回调地址的写法:网站地址/拦截器拦截的路径/服务提供商。
    创建完修改QQProperties类上的对应配置即可。
    测试通过,控制台打印了添加visitor数据的sql。

  • 相关阅读:
    JAVA【设计模式】享元模式
    独家巨献!阿里专家兼Github贡献者业“大师级Dubbo实战笔记”入门到成神
    容器化|自建 MySQL 集群迁移到 Kubernetes
    PCI 驱动编程基础
    4.1 Redis哨兵模式
    springboot项目打包成exe文件
    Redis的set数据类型——Redis
    明解STM32—GPIO应用设计篇之IO外部中断EXTI原理及使用方法
    ALL in Boom 日志记录 (ing ...
    IDEA常用快捷键大全
  • 原文地址:https://blog.csdn.net/heiye_007/article/details/130636157