本文章的代码在第二篇(Spring Security使用(二) 异步登录 | 代码日志 (fanxing.live))的代码上继续完成

腾讯互联

申请地址:QQ互联官网首页

接入教程:网站应用接入概述

创建QQLoginUtil类

application.properties

在配置文件内添加下面几个配置

这些配置的内容在腾讯互联申请的应用就能看到

qq.appid=******
qq.appkey=******
qq.redirect_uri=http://127.0.0.1:8080/QQLogin

QQLoginUtil.java

新建一个 util包 com.example.security2.util,在里面创建这个类

package com.example.security2.util;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Component
public class QQLoginUtil {@Value("${qq.appid}")private String appid = "101513767";@Value("${qq.appkey}")private String appkey = "b1d978cefcf405388893d8e686d307b0";@Value("${qq.redirect_uri}")private String redirect_uri = "http://127.0.0.1:8080/QQLogin";private ObjectMapper mapper;//生成登录链接public String getLoginUrl(Object state) throws UnsupportedEncodingException {StringBuffer url = new StringBuffer("https://graph.qq.com/oauth2.0/authorize?");url.append("response_type=code&");url.append("client_id="+appid);url.append("&redirect_uri="+ redirect_uri);url.append("&state="+state);System.out.println(url);return url.toString();}//获取CODEpublic Map getOpenId(String access_token) throws JsonProcessingException {String url = "https://graph.qq.com/oauth2.0/me";MultiValueMap params = new LinkedMultiValueMap();params.add("access_token",access_token);params.add("fmt","json");String json = httpClient(url,params);System.out.println(json);if(mapper == null){mapper = new ObjectMapper();}Map<String, Object> tmpMap=mapper.readValue(json, Map.class);System.out.println(tmpMap);return tmpMap;}public Map getAccessToken(String code) {String url = "https://graph.qq.com/oauth2.0/token";MultiValueMap params = new LinkedMultiValueMap();params.add("grant_type","authorization_code");params.add("client_id",appid);params.add("client_secret",appkey);params.add("code",code);params.add("redirect_uri",redirect_uri);params.add("fmt","json");String json = httpClient(url,params);System.out.println(json);if(mapper == null){mapper = new ObjectMapper();}Map<String, Object> tmpMap = null;try{tmpMap =mapper.readValue(json, Map.class);}catch (Exception ex){System.out.println("出错了");}return tmpMap;}//刷新access_token(自动续期)public Map getNewAccessToken(String refresh_token){String url = "https://graph.qq.com/oauth2.0/token";MultiValueMap params = new LinkedMultiValueMap();params.add("grant_type","refresh_token");params.add("client_id",appid);params.add("client_secret",appkey);params.add("refresh_token",refresh_token);String json = httpClient(url,params);if(mapper == null){mapper = new ObjectMapper();}Map<String, Object> tmpMap = null;try{tmpMap =mapper.readValue(json, Map.class);}catch (Exception ex){System.out.println("出错了");}return tmpMap;}//获取用户信息public Map getUserInfo(String access_token,String openid) throws JsonProcessingException {System.out.println("AccessToken="+access_token + ",OpenId="+openid);String url = "https://graph.qq.com/user/get_user_info";MultiValueMap params = new LinkedMultiValueMap();params.add("access_token",access_token);params.add("oauth_consumer_key",appid);params.add("openid",openid);String json = httpClient(url,params);if(mapper == null){mapper = new ObjectMapper();}Map<String, Object> tmpMap=mapper.readValue(json, Map.class);System.out.println(tmpMap);return tmpMap;}//直接一次获取到个人信息public Map getUserInfoPlus(String code){try{String acode = getAccessToken(code).get("access_toke").toString();String openid = getOpenId(acode).get("openid").toString();return getUserInfo(acode,openid);}catch (Exception ex){System.out.println("出问题了");return new HashMap();}}//发送请求public String httpClient(String url,MultiValueMap<String, String> params){RestTemplate client = new RestTemplate();HttpHeaders headers = new HttpHeaders();client.getMessageConverters().set(1,new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 支持中文编码HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(params, headers);ResponseEntity<String> response = client.exchange(url, HttpMethod.POST, requestEntity, String.class);return response.getBody();}}

测试是否可以获取到信息

在UserController类下面添加下面方法

@RequestMapping("/QQLogin")
public Map QQLogin(String code) {return qqLoginUtil.getUserInfoPlus(code);
}

然后在安全框架配置中 对QQLogin进行忽略,要不然还得登录

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/js/**","/QQLogin").permitAll()   //就在这一行里面添加.anyRequest().authenticated().and().formLogin().loginPage("/logintips").loginProcessingUrl("/check")  //异步校验.successHandler(successHandler).failureHandler(failHandle).permitAll().and().exceptionHandling().accessDeniedHandler(accessHandle).and().logout().permitAll();http.csrf().disable();
}

接下来测试访问下我们的登录地址,然后使用QQ登录后会打印到浏览器上什么信息

看上面图片,说明我们可以获取到个人信息了,这说明,我们的QQLoginUtil类写好了。

安全框架

QQAuthenticationFilter

自定义一个身份验证过滤器,这个过滤器用来过滤 /QQLogin这个路径的内容

package com.example.security2;import com.example.security2.util.QQLoginUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class QQAuthenticationFilter extends AbstractAuthenticationProcessingFilter {protected QQAuthenticationFilter(String defaultFilterProcessesUrl) {super(new AntPathRequestMatcher(defaultFilterProcessesUrl, "GET"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {String code = request.getParameter("code");System.out.println("获取的Code:" + code);// 生成验证 authenticationToken,这个传过去code只是把获取的这个想办法传过去UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(code, null);// 返回验证结果System.out.println("进入校验qq登录");return this.getAuthenticationManager().authenticate(authRequest);}
}

QQAuthenticationManager

package com.example.security2;import com.example.security2.pojo.User;
import com.example.security2.util.QQLoginUtil;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;import java.util.*;public class QQAuthenticationManager implements AuthenticationManager {@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {//这个getname方法就是获取的我们上面传过来的code,如果为null的话,直接抛出token异常if(authentication.getName() != null){try{//QQ登录操作类QQLoginUtil qqLoginUtil = new QQLoginUtil();Map access_token_get = qqLoginUtil.getAccessToken(authentication.getName());//通过操作类获取到access_token以及refresh_tokenString access_token = access_token_get.get("access_token").toString();String refresh_token = access_token_get.get("refresh_token").toString();//获取到openidString openid = qqLoginUtil.getOpenId(access_token).get("openid").toString();//判断是否成功获取到,其实access_token为null的话,直接就会报错了,直接抛出token异常,这一步要与不要都行if(access_token == null || openid == null){throw  new BadCredentialsException("Token is invalid");}//直接在这里赋值权限,给它管理员权限List<GrantedAuthority> authorities = new ArrayList<>();authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));//将这俩东西保存到一个map中,到handler那里好获取Map info = new HashMap();info.put("access_token",access_token);info.put("openid",openid);info.put("refresh_token",refresh_token);//返回这个认证的token到我们登录成功的Handler里,为什么是登录成功??因为,openid获取到了即使登录成功//其实这里少一步,就是这个openid应该给我们数据库的用户来对比一下,看看是否绑定,但这里是QQ直登,所以不需要我们的数据库就可以登录return new UsernamePasswordAuthenticationToken(info,null,authorities);}catch (Exception ex){throw  new BadCredentialsException("Token is invalid");}}throw  new BadCredentialsException("Token is invalid");}
}

refresh_token: 把这个也放到map里是因为,如果到时候access_token失效后,可以直接从SecurityContextHolder.getContext().getAuthentication().getName(); 这个方法中把这个map给获取出来,拿着这个refresh_token去刷新一遍

SecurityContextHolder.getContext().getAuthentication().getName();

示例内容

{access_token=9E716E069F480D05D1FB73B258F33242, refresh_token=C4536D2BFE439BCE6B898028909E2438, openid=CE722B59C6E0C995F5FD3D7013A0D271}

QQSusscessHandlerImpl

package com.example.security2;import com.example.security2.util.QQLoginUtil;
import com.example.security2.vo.ResultVO;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;public class QQSusscessHandlerImpl implements AuthenticationSuccessHandler {ObjectMapper objectMapper;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {}@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {//登录成功了这里让他转发到一个页面,我们用这个test,让他qq登录成功后直接跳转到这个request.getRequestDispatcher("/test").forward(request,response);}
}

配置

到这里我们所需要的类已经写完了,只要我们把写的这些装到我们的配置里,那么就可以做测试了

package com.example.security2;import com.example.security2.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;@Configurable
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredCustomUserDetailsService customUserDetailsService;@AutowiredSuccessHandlerImpl successHandler;@AutowiredFailHandleImpl failHandle;@AutowiredAccessHandleImpl accessHandle;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(customUserDetailsService).passwordEncoder(getPasswordEncoder());}public PasswordEncoder getPasswordEncoder(){return  new PasswordEncoder() {@Overridepublic String encode(CharSequence charSequence) {return charSequence.toString();}@Overridepublic boolean matches(CharSequence charSequence, String s) {return s.equals(charSequence.toString());}};}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/js/**").permitAll()  //.antMatchers("/js/**","/QQLogin").permitAll() 未修改前代码.anyRequest().authenticated().and().formLogin().loginPage("/logintips").loginProcessingUrl("/check")  //异步校验.successHandler(successHandler).failureHandler(failHandle).permitAll().and().exceptionHandling().accessDeniedHandler(accessHandle).and().logout().permitAll();http.csrf().disable();http.addFilterAt(qqAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); //使自定义的过滤器生效}//获取我们的自定义的过滤器,并且在其里面添加Manager校验以及登录成功后的过滤器private QQAuthenticationFilter qqAuthenticationFilter(){QQAuthenticationFilter authenticationFilter = new QQAuthenticationFilter("/QQLogin"); //这一行是让它拦截/QQLogin路径的地址,也就是我们的回调地址authenticationFilter.setAuthenticationManager(new QQAuthenticationManager());authenticationFilter.setAuthenticationSuccessHandler(new QQSusscessHandlerImpl());return authenticationFilter;}
}

测试

修改UserController类下的test

@RequestMapping("/test")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public Object Test(){return SecurityContextHolder.getContext().getAuthentication().getName();
}

由于我们是转发到了test页面,所以路径不会改变,但是内容却变成了我们想要获取的
如果项目里面接入了安全框架,这里应该要跳转我们的首页或者其他页面

欢迎访问:http://www.fanxing.live

Spring Security使用(三) 安全框架内使用QQ登录以及不加安全框架使用QQ登录相关推荐

  1. spring security 学习三-rememberMe

    spring security 学习三-rememberMe 功能:登录时的"记住我"功能 原理: rememberMeAuthenticationFilter在security过 ...

  2. SpringBoot整合Spring Security——第三章异常处理

    文章目录 一.常见异常 二.源码分析 三.处理异常 四.拓展spring security authenticationProvider用法及关闭不隐藏UserNotFoundException的解决 ...

  3. 深入浅出Spring Security(三):FilterChainProxy的运行过程

    上篇回顾 我们已经知道了Spring Security的核心过滤器的创建和原理,本文主要介绍核心过滤器FilterChainProxy是如何在tomcat的ServletContext中生效的. Se ...

  4. Spring Security(三十六):12. Spring MVC Test Integration

    Spring Security provides comprehensive integration with Spring MVC Test Spring Security提供与Spring MVC ...

  5. spring security(三)oauth2

    oauth2.0: 阮一峰理解OAuth 2.0:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html https://www.kancloud. ...

  6. Spring Security Oauth2 (三) 密码码模式

  7. Spring Security教程

    Spring Security教程 Web系统中登录认证(Authentication)的核心就是凭证机制,无论是Session还是JWT,都是在用户成功登录时返回给用户一个凭证,后续用户访问接口需携 ...

  8. Spring Security 安全框架

    Spring Security 一. Spring Security 简介 1 概括 Spring Security 是一个高度自定义的安全框架.利用 Spring IoC/DI和 AOP 功能,为系 ...

  9. 【Spring Security】Spring Security框架详解

    文章目录 前言 一.框架概述 Spring Security的架构 Spring Security的主要特点 二.认证 HTTP Basic认证 表单登录 OpenID Connect 三.授权 基于 ...

最新文章

  1. 刘宇凡:数字让切糕与电商溅起涟漪
  2. 《高阶Perl》——导读
  3. 一个多线程死锁案例,如何避免及解决死锁问题?
  4. tcp 服务端如何判断客户端断开连接
  5. Codeforces Round #530 (Div. 2)
  6. 小米air耳机重新配对_平价蓝牙耳机品牌,百元平价蓝牙耳机推荐
  7. 计算机三大科学理论是,近代科学最伟大的三大理论:进化论、量子论和计算论...
  8. 3.1. 一元、多元逻辑回归、tensorflow2实现——python实战
  9. c# printDialog不显示问题
  10. Linux指令:top
  11. 面试总结-接口测试面试题
  12. 理光Ricoh Aficio MP C7501SP 一体机驱动
  13. 2019最新-全国等级保护测评机构推荐目录
  14. 快速解决“多分类不平衡”问题
  15. python 批量转换docx只转换了一个出现pywintypes.com_error被调用的对象已与其客户端断开连接
  16. 如何快速实现西门子S7-200/300 PLC转Modbus-TCP协议与第三方数据对接
  17. python人像录制加声音_Python自动化测试入门必读
  18. 天猫精灵智能设备对接(3)
  19. 推荐一个超人气的类似MSN弹出的控件
  20. 叠加等边三角形的绘制 python_叠_叠是什么意思_叠字怎么读_叠的含义_叠字组词-新东方在线字典...

热门文章

  1. python画条形图并分类-python matplotlib库绘制条形图练习题
  2. P1.Pytorch环境的配置及安装(Configuration and installation of Pytorch)
  3. 2.Lambda表达式
  4. 排列组合解决方格走法_方方格子的便利:拆分数据到多行
  5. 会计基础-会计科目+会计账户+复式记账+会计分录+会计凭证
  6. 常用的连续概率分布汇总
  7. 恐怕你不确定自己喜欢做什么
  8. 跟着AI涨知识-量子纠缠
  9. vb outlook发邮件
  10. ipxe无盘服务器,ipxe uefi pxe HTTP启动文件及启动菜单的个人体会