本文章主要为了实现google关联用户的服务
如果仅仅需要使用google邮箱登录,建议由前端实现google登录
google相关文档:https://developers.google.com/identity/protocols/oauth2
可以自己去谷歌开发者平台注册账号,获取到clientId与clientSecret
接下来 我的做法是前端以下方法点击登录后端会重定向到谷歌的授权页面,等待用户授权登录
@Value("${google.clientId}")
private String clientId;
@Value("${google.clientSecret}")
private String clientSecret;
@Value("${google.redirectUri}")
private String redirectUri;
@Value("${google.userInfoUri}")
private String userInfoUri;
// 令牌有效期(默认30分钟)
@Value("${token.expireTime}")
private int expireTime;
@Autowired
private QUserService quserService;
@Autowired
private RedisCache redisCache;
@Autowired
private QVerificationTokenService qVerificationTokenService;
@GetMapping("/login")
public void getCode(String code, HttpServletRequest req, HttpServletResponse resp) throws GeneralSecurityException, IOException, ParseException {
System.out.println(code);
String refererUrl = req.getHeader("Referer") == null ? "https://qollect.cn" : req.getHeader("Referer");
log.info("google 认证登录开始 ######### code={},refererUrl={}",code,refererUrl);
String refererKey = SnowFlakeUtil.nextId().toString();
redisCache.setCacheObject(refererKey, refererUrl);
QUser userInfo = new QUser();
QVerificationToken qVerificationToken = new QVerificationToken();
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GoogleClientSecrets clientSecrets = new GoogleClientSecrets();
GoogleClientSecrets.Details details = new GoogleClientSecrets.Details();
details.setClientId(clientId);
details.setClientSecret(clientSecret);
clientSecrets.setInstalled(details);
GoogleIdToken googleIdToken;
List<String> scopes = Arrays.asList("https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "openid");
//获取token
if (StringUtils.isEmpty(code)) {
// 创建验证流程对象
GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow = new GoogleAuthorizationCodeFlow
.Builder(httpTransport, jsonFactory, clientSecrets, scopes)
// AccessType为离线offline,才能获得Refresh Token
.setAccessType("offline").build();
// 返回跳转登录请求
String uri = googleAuthorizationCodeFlow.newAuthorizationUrl().setState(refererKey).setRedirectUri(redirectUri).build();
userInfo.setUrl(uri);
resp.sendRedirect(uri);
log.info("google 认证登录重定向 ######### code={},refererUrl={},uri={}",code,refererUrl,uri);
}
}
接着用户点完授权后 谷歌会回调你传的回调地址并带code,拿到code后获取用户信息
@Value("${google.clientId}")
private String clientId;
@Value("${google.clientSecret}")
private String clientSecret;
// 令牌秘钥
@Value("${token.secret}")
private String secret;
@Value("${token.expireTime}")
private int expireTime;
@Autowired
private CookieUtils cookieUtils;
@GetMapping("/code")
public void getCode(HttpServletRequest req, HttpServletResponse resp) throws IOException, GeneralSecurityException, ParseException {
// 获取用户信息
String code = req.getParameter("code");
String refererKey = req.getParameter("state");
String refererUrl = redisCache.getCacheObject(refererKey);
log.info("已认证回调 ######### code={},refererKey={},refererUrl={}", code, refererKey, refererUrl);
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GoogleClientSecrets clientSecrets = new GoogleClientSecrets();
GoogleClientSecrets.Details details = new GoogleClientSecrets.Details();
details.setClientId(clientId);
details.setClientSecret(clientSecret);
clientSecrets.setInstalled(details);
List<String> scopes = Arrays.asList("https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "openid");
GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, clientSecrets, scopes)
// AccessType为离线offline,才能获得Refresh Token
.setAccessType("offline").build();
GoogleAuthorizationCodeTokenRequest tokenRequest = googleAuthorizationCodeFlow.newTokenRequest(code);
tokenRequest.setRedirectUri(redirectUri);
tokenRequest.setGrantType("authorization_code");
// 发起授权请求,获得Token和Refresh Token
GoogleTokenResponse tokenResponse = tokenRequest.execute();
String token = tokenResponse.getAccessToken();
String refreshToken = tokenResponse.getRefreshToken();
GoogleIdToken googleIdToken;
String userToken = null;
if (StringUtils.isNotBlank(tokenResponse.getIdToken())) {
GoogleIdTokenVerifier idTokenVerifier = new GoogleIdTokenVerifier.Builder(googleAuthorizationCodeFlow.getTransport(), googleAuthorizationCodeFlow.getJsonFactory()).build();
idTokenVerifier.verify(tokenResponse.getIdToken());
googleIdToken = idTokenVerifier.verify(tokenResponse.getIdToken());
if (googleIdToken != null && googleIdToken.getPayload() != null) {
QUser userInfo = quserService.getOne(new LambdaQueryWrapper<QUser>().eq(QUser::getEmail, googleIdToken.getPayload().getEmail()));
if (userInfo == null) {
userInfo = new QUser();
}
// 用户信息表
userInfo.setToken(token);
userInfo.setRefreshToken(refreshToken);
userInfo.setEmail(googleIdToken.getPayload().getEmail());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sd = simpleDateFormat.format(new Date());
userInfo.setCreateDate(simpleDateFormat.parse(sd));
userInfo.setUpdateDate(simpleDateFormat.parse(sd));
userInfo.setSources(SourcesConstants.SOURCES_GOOGLE);
// json
JSONObject json = new JSONObject(googleIdToken.getPayload());
userInfo.setUsername(json.getString("name") + "");
userInfo.setImage(json.getString("picture") + "");
if (userInfo.getId() == null) {
userInfo.setId(SnowFlakeUtil.nextId());
quserService.insertInfo(userInfo);
} else {
quserService.updateById(userInfo);
}
// 认证表
QVerificationToken qVerificationToken = new QVerificationToken();
qVerificationToken.setToken(token);
qVerificationToken.setIdentifier(googleIdToken.getPayload().getEmail());
qVerificationToken.setExpiresDate(simpleDateFormat.parse(sd));
qVerificationTokenService.insertInfo(qVerificationToken);
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
long nowTime = System.currentTimeMillis();
long expTime = 15 * 24 * 3600 * 1000;
String uToken = Jwts.builder().setClaims(claims).setExpiration(new Date(nowTime + expTime)).signWith(SignatureAlgorithm.HS512, secret).compact();
// 保存redis 缓存
redisCache.setCacheObject(uToken, userInfo.getId(), expireTime, TimeUnit.MINUTES);
// 保存 token 到 cookie
cookieUtils.addTokenCookie(resp, uToken);
log.info("已认证回调重定向跳转后写出数据到页面 ######### code={},refererKey={},refererUrl={},userToken={}", code, refererKey, refererUrl, uToken);
resp.sendRedirect(refererUrl);
}
}
}