1.QQ配置文件:
#第三方QQ登录配置
constants:
# QQ
qqAppId: ******(QQ平台的AppID)
qqAppSecret: **********(QQ平台的AppSecret)
qqRedirectUrl: ***********(登录成功后的回调路径:http://ucapi.****.com/api/v1/anon/qq_login)
qqTokenUrl: https://graph.qq.com/oauth2.0/token?(QQtoken路径)
qqUrl: https://graph.qq.com/oauth2.0/authorize?(QQ访问路径)
clientId: ****-****-***-***-***** (客户端id)
url: http://uc.***.com (跳转首页,注册页的路径域名)
2.QQ第三方登录:常量配置类
package com.ciip.cloud.core.usercenter.constants;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.validation.constraints.NotEmpty;
/**
* Title: ThirdLoginConstants
* Description: 第三方QQ登录:常量配置类
*
* @author
* @created
*/
@Configuration
@ConfigurationProperties(prefix = "constants")
@Getter
@Setter
public class ThirdLoginConstants {
/**
* QQ的AppID
*/
@NotEmpty
private String qqAppId;
/**
* QQ的AppSecret
*/
@NotEmpty
private String qqAppSecret;
/**
* QQ的回调路径
*/
@NotEmpty
private String qqRedirectUrl;
/**
* QQtoken路径
*
*/
@NotEmpty
private String qqTokenUrl;
/**
* QQ访问路径
*
*/
@NotEmpty
private String qqUrl;
/**
* clientId
*
*/
@NotEmpty
private String clientId;
/**
* 域名路径
*
*/
@NotEmpty
private String url;
}
3.controller层:
package com.ciip.cloud.core.usercenter.controller;
/**
* Title: ThirdLoginController
* Description: 第三方QQ登录
*
* @author
* @created
*/
import com.ciip.cloud.core.common.constant.usercenter.CIIPCommonConstant;
import com.ciip.cloud.core.usercenter.service.QQAuthService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
@Api(value = "ThirdLoginController", description = "第三方登录(微信,QQ,微博)")
@RestController
@RequestMapping(value = CIIPCommonConstant.ApiPath.V1, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public class ThirdLoginController {
@Autowired
QQAuthService qqAuthService;
/**
* CIIP改版:QQ第三方登录页面
*
* @return
*/
@GetMapping(value = CIIPCommonConstant.ApiAuth.ANON +"/qq_loginUrl")
public String getQQCode(){
return qqAuthService.getQQCode();
}
/**
* CIIP改版:获取QQ登录后的token,openid等值
*
* @param code
* @param response
* @return
*/
@PostMapping(value = CIIPCommonConstant.ApiAuth.ANON +"/qq_accessToken")
public String getQQAccessToken(String code,HttpServletResponse response){
return qqAuthService.getQQAccessToken(code);
}
}
4.service层:
package com.ciip.cloud.core.usercenter.service;
/**
* Title: QQAuthService
* Description: QQ 认证接口
*
* @author
* @created 2019/5/8 17:30
*/
public interface QQAuthService {
/**
* CIIP 改版:获取 Code,即登陆页面的 URL
*
*
* @return
*/
String getQQCode();
/**
* CIIP 改版:获取token,该步骤返回的token期限为一个月
*
* @return
*/
String getQQAccessToken(String code);
/**
* CIIP 改版:根据accessToken,获取QQ的openId
*
*
* @param accessToken
* @return
*/
String getOpenId(String accessToken);
}
5.service实现层:
package com.ciip.cloud.core.usercenter.service.impl;
import com.ciip.cloud.core.usercenter.constants.ThirdLoginConstants;
import com.ciip.cloud.core.usercenter.model.CiipTokenLog;
import com.ciip.cloud.core.usercenter.service.CiipTokenLogService;
import com.ciip.cloud.core.usercenter.service.QQAuthService;
import com.ciip.cloud.core.usercenter.utils.HttpClientUtils;
import com.ciip.cloud.core.usercenter.utils.URLEncodeUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.InputStream;
/**
* Title: QQAuthServiceImpl
* Description: TODO
*
* @author
* @created
*/
@Service
public class QQAuthServiceImpl implements QQAuthService {
@Autowired
ThirdLoginConstants thirdLoginConstants;
@Autowired
CiipTokenLogService ciipTokenLogService;
/**
*获取 Code,即登陆页面的 URL
*
*
* @return
*/
@Override
public String getQQCode() {
//拼接url
StringBuilder url = new StringBuilder();
//url.append("https://graph.qq.com/oauth2.0/authorize?");
url.append(thirdLoginConstants.getQqUrl());
url.append("response_type=code");
url.append("&client_id=").append(thirdLoginConstants.getQqAppId());
//回调地址 ,回调地址要进行Encode转码
String redirect_uri = thirdLoginConstants.getQqRedirectUrl();
//转码
url.append("&redirect_uri=").append(URLEncodeUtil.getURLEncoderString(redirect_uri));
url.append("&state=ok");
System.out.println(url.toString());
return url.toString();
}
/**
* 获取token,该步骤返回的token期限为一个月
*
* @return
*/
@Override
public String getQQAccessToken(String code) {
if("".equals(code)){
return "code为空.";
}
StringBuilder url = new StringBuilder();
//url.append("https://graph.qq.com/oauth2.0/token?");
url.append(thirdLoginConstants.getQqTokenUrl());
url.append("grant_type=authorization_code");
url.append("&client_id=").append(thirdLoginConstants.getQqAppId());
url.append("&client_secret=").append(thirdLoginConstants.getQqAppSecret());
url.append("&code=").append(code);
//回调地址
String redirect_uri = thirdLoginConstants.getQqRedirectUrl();
//转码
url.append("&redirect_uri=").append(URLEncodeUtil.getURLEncoderString(redirect_uri));
String result = null;
try {
result = HttpClientUtils.get(url.toString(),"UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("url:" + url.toString());
//把token保存
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(result, "&");
String accessToken = StringUtils.substringAfterLast(items[0], "=");
Integer expiresIn = new Integer(StringUtils.substringAfterLast(items[1], "="));
String refreshToken = StringUtils.substringAfterLast(items[2], "=");
//获取openId
String openId = this.getOpenId(accessToken);
//token 信息的添加
CiipTokenLog ciipTokenLog = new CiipTokenLog();
ciipTokenLog.setAccessToken(accessToken);
ciipTokenLog.setExpiresIn(expiresIn);
ciipTokenLog.setRefreshToken(refreshToken);
ciipTokenLog.setOpenId(openId);
ciipTokenLogService.saveCiipTokenLog(ciipTokenLog);
return openId;
}
/**
* CIIP 改版:根据accessToken,获取QQ的openId
*
*
* @param accessToken
* @return
*/
@Override
public String getOpenId(String accessToken) {
StringBuilder url = new StringBuilder("https://graph.qq.com/oauth2.0/me?");
//获取保存的用户的token
if (!StringUtils.isNotEmpty(accessToken)){
return "未授权";
}
url.append("access_token=").append(accessToken);
String result = null;
try {
result = HttpClientUtils.get(url.toString(),"UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
String openId = StringUtils.substringBetween(result, "\"openid\":\"", "\"}");
System.out.println(openId);
return openId;
}
}
6.安全配置(SecurityConfig):
package com.ciip.cloud.core.usercenter.oauth;
import com.ciip.cloud.core.usercenter.handler.CustomAccessDeniedHandler;
import com.ciip.cloud.core.usercenter.oauth.moblie.SmsAuthenticationConfig;
import com.ciip.cloud.core.usercenter.oauth.moblie.SmsCodeFilter;
import com.ciip.cloud.core.usercenter.oauth.qq.QQCodeFilter;
import com.ciip.cloud.core.usercenter.oauth.qq.QqAuthenticationConfig;
import com.ciip.cloud.core.usercenter.oauth.weiBo.WeiBoAuthenticationConfig;
import com.ciip.cloud.core.usercenter.oauth.weiBo.WeiBoCodeFilter;
import com.ciip.cloud.core.usercenter.oauth.weiXin.WeiXinAuthenticationConfig;
import com.ciip.cloud.core.usercenter.oauth.weiXin.WeiXinCodeFilter;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Autowired
CustomAccessDeniedHandler customAccessDeniedHandler;
@Autowired
private SmsAuthenticationConfig smsAuthenticationConfig;
@Autowired
private QqAuthenticationConfig qqAuthenticationConfig;
@Autowired
private WeiXinAuthenticationConfig weiXinAuthenticationConfig;
@Autowired
private WeiBoAuthenticationConfig weiBoAuthenticationConfig;
@Autowired
private SmsCodeFilter smsCodeFilter;
@Autowired
private WeiXinCodeFilter weiXinCodeFilter;
@Autowired
private WeiBoCodeFilter weiBoCodeFilter;
@Autowired
private QQCodeFilter qqCodeFilter;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception { // @formatter:off
http
// 头部缓存
.headers()
.cacheControl()
.and()
// 防止网站被人嵌套
.frameOptions()
.sameOrigin();
http
.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器
.addFilterBefore(weiXinCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加weixin验证码校验过滤器
.addFilterBefore(weiBoCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加weiBo验证码校验过滤器
.addFilterBefore(qqCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加QQ验证码校验过滤器
.formLogin().and()
.authorizeRequests() // 授权配置
.antMatchers("/login","/api/v1/anon/**","/oauth/authorize","/oauth/token").permitAll() // 无需认证的请求路径
.and()
.csrf().disable()
.apply(smsAuthenticationConfig)//短信验证
.and().apply(qqAuthenticationConfig) // 第三方QQ登录验证
.and().apply(weiXinAuthenticationConfig) // 第三方WeiXin登录验证
.and().apply(weiBoAuthenticationConfig); // 第三方微博登录验证
http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { // @formatter:off
auth.userDetailsService(userDetailsService).passwordEncoder(customPasswordEncoder());
} // @formatter:on
//TODO 为了符合原有BIM用户加密方式,所以自定义加密规则
@Bean
public PasswordEncoder customPasswordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return DigestUtils.md5Hex(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return DigestUtils.md5Hex(rawPassword.toString()).equals(encodedPassword);
}
};
}
//TODO 采用内置加密方式,存储数据库格式如:'{MD5}e10adc3949ba59abbe56e057f20f883e'
@Bean
PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
7.QQ身份验证配置:
package com.ciip.cloud.core.usercenter.oauth.qq;
import com.ciip.cloud.core.usercenter.handler.CustomAuthenticationFailureHandler;
import com.ciip.cloud.core.usercenter.service.CiipUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
/**
* Title: QqAuthenticationConfig
* Description: TODO
*
* @author
* @created 2019/5/8 17:10
*/
@Component
public class QqAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Autowired
QqLoginSuccessHandler smsLoginSuccessHandler;
@Autowired
private CiipUserService userService;
@Override
public void configure(HttpSecurity http) throws Exception {
QqAuthenticationFilter smsAuthenticationFilter = new QqAuthenticationFilter();
smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsAuthenticationFilter.setAuthenticationSuccessHandler(smsLoginSuccessHandler);
smsAuthenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
QqAuthenticationProvider smsAuthenticationProvider = new QqAuthenticationProvider();
smsAuthenticationProvider.setUserService(userService);
http.authenticationProvider(smsAuthenticationProvider)
.addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
8.QQ身份验证筛选器:(验证是否通过)
package com.ciip.cloud.core.usercenter.oauth.qq;
import com.ciip.cloud.core.common.constant.usercenter.AddableHttpRequest;
import com.ciip.cloud.core.common.constant.usercenter.CIIPCommonConstant;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Title: QqAuthenticationFilter
* Description: TODO
*
* @author
* @created
*/
public class QqAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private boolean postOnly = true;
public QqAuthenticationFilter() {
super(new AntPathRequestMatcher(CIIPCommonConstant.OAUTH_LOGIN_URL.QQ_LOGIN, HttpMethod.POST.name()));
}
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
//获取openId
AddableHttpRequest addableHttpRequest = ((AddableHttpRequest) request);
String openId = addableHttpRequest.getParameter("openId");
//进行登录认证
QqAuthenticationToken authRequest = new QqAuthenticationToken(openId);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request,
QqAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
}
9.QQ身份验证提供程序:
package com.ciip.cloud.core.usercenter.oauth.qq;
import com.ciip.cloud.core.usercenter.service.CiipUserService;
import com.ciip.cloud.core.usercenter.vo.UserLoginVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* Title: QqAuthenticationProvider
* Description: TODO
*
* @author
* @created 2019/5/8 16:58
*/
public class QqAuthenticationProvider implements AuthenticationProvider {
private CiipUserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
QqAuthenticationToken authenticationToken = (QqAuthenticationToken) authentication;
String openId =(String) authenticationToken.getPrincipal();
UserDetails userDetails = userService.findByOpenId(openId);
if (userDetails == null)
throw new InternalAuthenticationServiceException("未找到与该QQ号对应的用户");
QqAuthenticationToken authenticationResult = new QqAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> aClass) {
return QqAuthenticationToken.class.isAssignableFrom(aClass);
}
public CiipUserService getUserService() {
return userService;
}
public void setUserService(CiipUserService userService) {
this.userService = userService;
}
}
10.QQ身份验证令牌:
package com.ciip.cloud.core.usercenter.oauth.qq;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import java.util.Collection;
/**
* Title: QqAuthenticationToken
* Description: TODO
*
* @author
* @created 2019/5/8 16:57
*/
public class QqAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
public QqAuthenticationToken(Object openId) {
super(null);
this.principal = openId;
setAuthenticated(false);
}
public QqAuthenticationToken(Object principal,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true);
}
public Object getPrincipal() {
return this.principal;
}
@Override
public Object getCredentials() {
return null;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}
11.QQ前置过滤器:
package com.ciip.cloud.core.usercenter.oauth.qq;
import com.alibaba.fastjson.JSON;
import com.ciip.cloud.core.common.constant.usercenter.AddableHttpRequest;
import com.ciip.cloud.core.common.constant.usercenter.CIIPCommonConstant;
import com.ciip.cloud.core.common.vo.ResultUtil;
import com.ciip.cloud.core.common.vo.usercenter.OauthCode;
import com.ciip.cloud.core.usercenter.exception.ValidateCodeException;
import com.ciip.cloud.core.usercenter.handler.CustomAuthenticationFailureHandler;
import com.ciip.cloud.core.usercenter.model.CiipUser;
import com.ciip.cloud.core.usercenter.service.CiipUserService;
import com.ciip.cloud.core.usercenter.service.QQAuthService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Title: QQCodeFilter
* Description: TODO
*
* @author
* @created 2019/5/27 9:37
*/
@Component
public class QQCodeFilter extends OncePerRequestFilter {
@Autowired
private CustomAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private CiipUserService ciipUserService;
@Autowired
QQAuthService qqAuthService;
/**
* 过滤器:
* 1.分为登录成功直接跳转到登录页面,登录失败表示未绑定跳转到注册页面
* 2.如果已经登录,则可以进行绑定,并且验证一个QQ号只能绑定一个账号
*
*
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String openId ="";
if (StringUtils.equalsIgnoreCase(request.getContextPath()+ CIIPCommonConstant.OAUTH_LOGIN_URL.QQ_LOGIN, request.getRequestURI()) && StringUtils.equalsIgnoreCase(request.getMethod(), HttpMethod.POST.name())) {
//1.根据code,获取openId
String code = request.getParameter("code");
openId = qqAuthService.getQQAccessToken(code);
//2.将openId放入request请求对象中,并进行验证
AddableHttpRequest wrappedRequest = new AddableHttpRequest(request);
wrappedRequest.addParameter("openId",openId);
//3.获取当前用户【登录用户】
CiipUser ciipUser = ciipUserService.getCurrent();
//4.不为空,表示为登录状态,用于绑定用户
if(null != ciipUser && ("".equals(ciipUser.getOpenId()) || null == ciipUser.getOpenId())){
//5.绑定前验证,根据openId,获取对象【防止一个QQ绑定多个用户】
CiipUser ciipUserInfo = ciipUserService.findCiipUserByOpenId(openId);
//不为空,表示该QQ号已经绑定其他用户,跳转到绑定页面(status=false:表示不能绑定,前端根据该参数给出“您的QQ已绑定过了,请核实.”的提示,name=QQ:该参数来区分微信,QQ,微博类型)
if(null != ciipUserInfo){
response.sendRedirect(thirdLoginConstants.getUrl()+"/account/binding?status=false&name=QQ");
return;
}
ciipUser.setOpenId(openId);
//绑定,给用户表中添加QQId,表示绑定成功
ciipUserService.addCiipUser(ciipUser);
//添加系统消息--QQ绑定成功
CiipUserMessage ciipUserMessage = new CiipUserMessage();
Map<String,Object> paramsMap = new HashMap<String, Object>();
ciipUserMessage.setCreateDate(new Date());
ciipUserMessage.setSoruceId(ciipUser.getId());
ciipUserMessage.setCreateUserId(ciipUser.getId());
ciipUserMessage.setCreateUserName(ciipUser.getUsername());
ciipUserMessage.setReceiveUserId(ciipUser.getId());
ciipUserMessage.setMessageBusinessType(CiipMessageBusinessType.BINDING_QQ);
ciipUserMessage.setMessageType(CiipMessageType.SYSTEM);
ciipUserMessage.setMsgTitle("QQ绑定成功");
ciipUserMessage.setMsgBody(StrUtil.format(CiipUserMessageTemplate.getOperationInfo(17),paramsMap));
ciipUserMessageService.addMessage(ciipUserMessage);
response.setHeader("Content-Type", "application/json;charset=UTF-8");
//跳转到绑定页面,绑定成功
response.sendRedirect(thirdLoginConstants.getUrl()+"/account/binding");
//byte[] bytes = JSON.toJSONBytes(ResultUtil.error(OauthCode.BINDING_SUCCESS.getCode(), OauthCode.BINDING_SUCCESS.getMsg(), request.getHeader("referer")));
//response.getOutputStream().write(bytes);
return;
}else{
//为空,表示未登录,则登录前进行验证
try {
validateQQCode(openId);
} catch (ValidateCodeException e) {
authenticationFailureHandler.onAuthenticationFailure(request, response, e);
return;
}
}
filterChain.doFilter(wrappedRequest, response);
}else{
filterChain.doFilter(request, response);
}
}
private void validateQQCode(String openId) throws ServletRequestBindingException, ValidateCodeException {
//根据openId,获取用户信息
UserDetails userDetails = ciipUserService.findByOpenId(openId);
//表示该用户不存在,跳转到注册页面
if(null == userDetails){
throw new ValidateCodeException("该用户与QQ没有绑定或该用户不存在.");
}
//登录成功后,修改上次登录时间
CiipUser ciipUser = ciipUserService.findCiipUserByOpenId(openId);
if(ciipUser != null){
if(null != ciipUser.getLoginDate() && !"".equals(ciipUser.getLoginDate())){
ciipUser.setLastLoggedDate(ciipUser.getLoginDate());
}else{
ciipUser.setLastLoggedDate(new Date());
}
ciipUser.setLoginDate(new Date());
ciipUserRepository.saveAndFlush(ciipUser);
}
}
}
12.QQ登录成功处理程序:
package com.ciip.cloud.core.usercenter.oauth.qq;
import cn.hutool.core.lang.Console;
import cn.hutool.core.map.MapUtil;
import com.ciip.cloud.core.common.constant.enums.usercenter.CiipOperationMethod;
import com.ciip.cloud.core.common.constant.enums.usercenter.CiipOperationRecordType;
import com.ciip.cloud.core.common.constant.usercenter.CIIPCommonConstant;
import com.ciip.cloud.core.common.vo.ResultUtil;
import com.ciip.cloud.core.common.vo.usercenter.OauthCode;
import com.ciip.cloud.core.usercenter.service.CiipUserOperationLogService;
import com.ciip.cloud.core.usercenter.service.CiipUserService;
import com.ciip.cloud.core.usercenter.vo.CiipUserInfo;
import com.ciip.cloud.core.usercenter.vo.UserInfo;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Title: QqLoginSuccessHandler
* Description: TODO
*
* @author
* @created 2019/5/8 18:01
*/
@Component
public class QqLoginSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Autowired
private CiipUserOperationLogService ciipUserOperationLogService;
private String redirectUrl ="/";
@Autowired
private CiipUserService ciipUserService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
try {
String clientId = request.getParameter("clientId");
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
TokenRequest tokenRequest = new TokenRequest(MapUtil.newHashMap(), clientId, clientDetails.getScope(), "mobile");
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
Console.log("获取token 成功:{}", oAuth2AccessToken.getValue());
UserInfo user =(UserInfo)authentication.getPrincipal();
CiipUserInfo ciipUserInfo = ciipUserService.getAuthUserInfo(Long.valueOf(user.getUserId()));
//用户日志添加操作记录
Map<String,Object> paramsMap = new HashMap<String,Object>();
paramsMap.put("currentUser", user.getUsername());
ciipUserOperationLogService.addUserOperationLog(Long.valueOf(user.getUserId()),String.valueOf(user.getUserId()), CiipOperationRecordType.WEB_LOGIN.name(), CiipOperationMethod.QQ_LOGIN.name(), 11,paramsMap);
//添加header认证信息
response.setHeader(CIIPCommonConstant.AUTHORIZATION_HEADER, String.format("%s %s", CIIPCommonConstant.BEARER_TOKEN_TYPE, oAuth2AccessToken.getValue().toString()));
request.getSession().setAttribute(CIIPCommonConstant.BEARER_TOKEN,oAuth2AccessToken.getValue().toString());
response.setHeader("Content-Type", "application/json;charset=UTF-8");
//该方法为实现单点登录
redisUtil.setWithExpireTime(CIIPCommonConstant.CIIP_AUTH,request.getSession().getId(), oAuth2AccessToken.getValue(),oAuth2AccessToken.getExpiresIn());
//登录成功返回到首页
response.sendRedirect(thirdLoginConstants.getUrl()+"/login");
} catch (IOException e) {
throw new BadCredentialsException(
"Failed to decode basic authentication token");
}
}
}
13.HttpServletRequestWrapper的封装,用户request中添加参数
package com.ciip.cloud.core.common.constant.usercenter;
/**
* Title: AddableHttpRequest
* Description: TODO
*
* @author
* @created 2019/5/28 15:12
*/
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;
public class AddableHttpRequest extends HttpServletRequestWrapper {
private HttpServletRequest wrapped;
private Map<String, String[]> parameterMap;
public AddableHttpRequest(HttpServletRequest wrapped) {
super(wrapped);
this.wrapped = wrapped;
}
public void addParameter(String name, String value) {
if (parameterMap == null) {
parameterMap = new HashMap<String, String[]>();
parameterMap.putAll(wrapped.getParameterMap());
}
String[] values = parameterMap.get(name);
if (values == null) {
values = new String[0];
}
List<String> list = new ArrayList<String>(values.length + 1);
list.addAll(Arrays.asList(values));
if(!"".equals(value)){
list.add(value);
}
parameterMap.put(name, list.toArray(new String[0]));
}
@Override
public String getParameter(String name) {
if (parameterMap == null) {
return wrapped.getParameter(name);
}
String[] strings = parameterMap.get(name);
if (strings != null) {
return strings[0];
}
return null;
}
@Override
public Map<String, String[]> getParameterMap() {
if (parameterMap == null) {
return wrapped.getParameterMap();
}
return Collections.unmodifiableMap(parameterMap);
}
@Override
public Enumeration<String> getParameterNames() {
if (parameterMap == null) {
return wrapped.getParameterNames();
}
return Collections.enumeration(parameterMap.keySet());
}
@Override
public String[] getParameterValues(String name) {
if (parameterMap == null) {
return wrapped.getParameterValues(name);
}
return parameterMap.get(name);
}
}
14.自定义身份验证失败处理程序:
package com.ciip.cloud.core.usercenter.handler;
import com.alibaba.fastjson.JSON;
import com.ciip.cloud.core.common.constant.usercenter.CIIPCommonConstant;
import com.ciip.cloud.core.common.vo.ResultUtil;
import com.ciip.cloud.core.common.vo.usercenter.OauthCode;
import com.ciip.cloud.core.usercenter.service.CiipLoginHistoryService;
import com.ciip.cloud.core.usercenter.service.CiipUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private Logger log = LoggerFactory.getLogger(this.getClass());
private String failureUrl = "/signIn";
@Autowired
CiipUserService ciipUserService;
@Autowired
CiipLoginHistoryService ciipLoginHistoryService;
@Override
public void onAuthenticationFailure(HttpServletRequest request,HttpServletResponse response, AuthenticationException exception) throws IOException {
AddableHttpRequest addableHttpRequest = ((AddableHttpRequest) request);
String openId = addableHttpRequest.getParameter("openId");
String type = addableHttpRequest.getParameter("type");
response.setHeader("Content-Type", "application/json;charset=UTF-8");
log.info("--------------未绑定,经跳转到注册页面--------------");
log.info("--------------openId--------------"+openId);
log.info("--------------type--------------"+type);
//跳转到注册页面,并带openId参数用于注册时直接绑定QQ账号,type参数是微信,QQ,微博类型
response.sendRedirect(thirdLoginConstants.getUrl()+"/register?id="+ EncryptUtil.encrypt(openId)+"&type="+type);
//byte[] bytes = JSON.toJSONBytes(ResultUtil.error(OauthCode.USERCENTER_OAUTH_LOGIN_ERROR.getCode(),OauthCode.USERCENTER_OAUTH_LOGIN_ERROR.getMsg()+""+exception.getMessage(),request.getHeader("referer")));
//response.getOutputStream().write(bytes);
}
}
15.验证异常:
package com.ciip.cloud.core.usercenter.exception;
import org.springframework.security.core.AuthenticationException;
/**
* Title: ValidateCodeException
* Description: TODO
*
* @author
* @created 2019/5/8 18:33
*/
public class ValidateCodeException extends AuthenticationException {
public ValidateCodeException(String message) {
super(message);
}
}