接着上一篇文章SpringBoot网站添加第三方登录之QQ登录,本文介绍第三方登录之 GitHub。
GitHub 授权相对比较简单,因为无需审核,分分钟搞定。
1、登录 github 后台,创建应用
地址:https://github.com/settings/developers
或者在个人设置里,点最下面那个开发者设置
进去后,创建一个应用。如图,我已经创建了一个应用
查看应用,可以看到应用的id和密码
这里没什么好说的,跟 QQ 登录类似
2、授权的基本原理
可以参考官方文档
1)根据 GitHub 登录链接可以回调获得 code
2)根据Client ID 、Client Secret 和 code 可获得 token
3)根据 token 获得 用户信息
其中 id 是一个唯一的值,比如上面的 25662729
3、几个必要的URL
参考官方文档
1)登录页面授权 URL:
https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&state=%s
2)获得 Token 的 URL:
https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&state=%s
3)获得用户信息的 URL:
https://api.github.com/user?access_token=%s
1、数据库设计
直接看图片
2、放置 Github 登录按钮
这里有两种常用的方式
① 弹小窗
② 在新窗口打开
点击链接后,跳到授权页面
参考上一篇文章的 QQ 登录
1、由于做了多个登录,所以代码做了一定程度的封装,大致如下:
2、由于全部是自己封装的,所以http请求的代码也是所有的登录共用的,这里统一放放到了类DefaultAuthServiceImpl中,代码如下:
由此,所有的登录Service只需要继承AuthService即可。
3、GithubAuthService.java
4、GithubAuthServiceImpl.java
5、Controller,根据项目不同,自己修改
这里把qq登录和github登录,未登录之前绑定,登录后再后台绑定融合到了一起。
GitHub 授权相对比较简单,因为无需审核,分分钟搞定。
一、创建应用
1、登录 github 后台,创建应用
地址:https://github.com/settings/developers
或者在个人设置里,点最下面那个开发者设置
进去后,创建一个应用。如图,我已经创建了一个应用
查看应用,可以看到应用的id和密码
这里没什么好说的,跟 QQ 登录类似
2、授权的基本原理
可以参考官方文档
1)根据 GitHub 登录链接可以回调获得 code
2)根据Client ID 、Client Secret 和 code 可获得 token
3)根据 token 获得 用户信息
其中 id 是一个唯一的值,比如上面的 25662729
3、几个必要的URL
参考官方文档
1)登录页面授权 URL:
https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&state=%s
2)获得 Token 的 URL:
https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&state=%s
3)获得用户信息的 URL:
https://api.github.com/user?access_token=%s
二、基本准备
1、数据库设计
直接看图片
2、放置 Github 登录按钮
这里有两种常用的方式
① 弹小窗
- <script>
- function openWin(url,name,iWidth,iHeight) {
- //获得窗口的垂直位置
- var iTop = (window.screen.availHeight - 30 - iHeight) / 2;
- //获得窗口的水平位置
- var iLeft = (window.screen.availWidth - 10 - iWidth) / 2;
- window.open(url, name, 'height=' + iHeight + ',innerHeight=' + iHeight + ',width=' + iWidth + ',innerWidth=' + iWidth + ',top=' + iTop + ',left=' + iLeft + ',status=no,toolbar=no,menubar=no,location=no,resizable=no,scrollbars=0,titlebar=no');
- }
- function qqLogin() {
- var url = "https://github.com/login/oauth/authorize?client_id=96bb0592e6b3b8f2fb90&redirect_uri=http://codergroup.cn/oauth/github/callback&state=LiuYanzhaoLoveLuoQiNeverGiveUp";
- openWin(url,"qqLogin",650,500);
- }
- </script>
- <a href="javascript:void(0);" onclick="qqLogin()"></a>
② 在新窗口打开
- <a href="https://github.com/login/oauth/authorize?client_id=96bb0511e6b3b8f2fb90&redirect_uri=http://codergroup.cn/oauth/github/callback&state=use-login" target="_blank"></a>
点击链接后,跳到授权页面
三、具体代码
参考上一篇文章的 QQ 登录
1、由于做了多个登录,所以代码做了一定程度的封装,大致如下:
- package com.liuyanzhao.sens.service;
- import com.liuyanzhao.sens.model.dto.BindUserDTO;
- import com.liuyanzhao.sens.model.dto.JsonResult;
- import com.liuyanzhao.sens.utils.Response;
- import java.io.UnsupportedEncodingException;
- /**
- * @author 言曌
- * @date 2019/1/20 上午10:24
- */
- public interface AuthService {
- /**
- * 根据code获得Token
- *
- * @param code code
- * @return token
- */
- Response<String> getAccessToken(String code);
- /**
- * 根据Token获得OpenId
- *
- * @param accessToken Token
- * @return openId
- */
- Response<String> getOpenId(String accessToken);
- /**
- * 刷新Token
- *
- * @param code code
- * @return 新的token
- */
- Response<String> refreshToken(String code);
- /**
- * 拼接授权URL
- *
- * @return URL
- */
- Response<String> getAuthorizationUrl();
- /**
- * 根据Token和OpenId获得用户信息
- *
- * @param accessToken Token
- * @param openId openId
- * @return 第三方应用给的用户信息
- */
- Response<BindUserDTO> getUserInfo(String accessToken, String openId);
- }
2、由于全部是自己封装的,所以http请求的代码也是所有的登录共用的,这里统一放放到了类DefaultAuthServiceImpl中,代码如下:
- package com.liuyanzhao.sens.service.impl;
- import com.liuyanzhao.sens.service.AuthService;
- import org.springframework.http.client.SimpleClientHttpRequestFactory;
- import org.springframework.http.converter.ByteArrayHttpMessageConverter;
- import org.springframework.http.converter.HttpMessageConverter;
- import org.springframework.http.converter.ResourceHttpMessageConverter;
- import org.springframework.http.converter.StringHttpMessageConverter;
- import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
- import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
- import org.springframework.http.converter.xml.SourceHttpMessageConverter;
- import org.springframework.web.client.RestTemplate;
- import javax.xml.transform.Source;
- import java.nio.charset.StandardCharsets;
- import java.util.LinkedList;
- import java.util.List;
- /**
- * @author 言曌
- * @date 2018/5/9 下午3:02
- */
- public abstract class DefaultAuthServiceImpl implements AuthService {
- public static RestTemplate getRestTemplate() {// 手动添加
- SimpleClientHttpRequestFactory requestFactory=new SimpleClientHttpRequestFactory();
- requestFactory.setReadTimeout(120000);
- List<HttpMessageConverter<?>> messageConverters = new LinkedList<>();
- messageConverters.add(new ByteArrayHttpMessageConverter());
- messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
- messageConverters.add(new ResourceHttpMessageConverter());
- messageConverters.add(new SourceHttpMessageConverter<Source>());
- messageConverters.add(new AllEncompassingFormHttpMessageConverter());
- messageConverters.add(new MappingJackson2HttpMessageConverter());
- RestTemplate restTemplate=new RestTemplate(messageConverters);
- restTemplate.setRequestFactory(requestFactory);
- return restTemplate;
- }
- }
由此,所有的登录Service只需要继承AuthService即可。
3、GithubAuthService.java
- package com.liuyanzhao.sens.service;
- /**
- * @author 言曌
- * @date 2018/5/15 下午11:28
- */
- public interface GithubAuthService extends AuthService {
- }
4、GithubAuthServiceImpl.java
- package com.liuyanzhao.sens.service.impl;
- import com.alibaba.fastjson.JSONObject;
- import com.liuyanzhao.sens.model.dto.BindUserDTO;
- import com.liuyanzhao.sens.service.GithubAuthService;
- import com.liuyanzhao.sens.utils.Response;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
- import org.springframework.web.util.UriComponentsBuilder;
- import java.net.URI;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * @author 言曌
- * @date 2018/5/15 下午11:31
- */
- @Service
- @Slf4j
- public class GithubAuthServiceImpl extends DefaultAuthServiceImpl implements GithubAuthService {
- private static final String AUTHORIZE_URL = "https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&state=%s";
- private static final String ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&state=%s";
- private static final String USER_INFO_URL = "https://api.github.com/user?access_token=%s";
- // 下面的属性可以通过配置读取
- private static final String CALLBACK_URL = "http://localhost:8080/oauth/github/callback";//回调地址
- private static final String API_KEY = "11111111111111111";//Client ID
- private static final String API_SECRET = "1111111111111111111111111111111";//Client Secret
- private static final String GITHUB_STATE = "use-login";//state,随便填,会返回原值给你
- //此处是获取key-value类型的参数
- private Map<String, String> getParam(String string) {
- Map<String, String> map = new HashMap();
- String[] kvArray = string.split("&");
- for (int i = 0; i < kvArray.length; i++) {
- String[] kv = kvArray[i].split("=");
- if (kv.length == 2) {
- map.put(kv[0], kv[1]);
- } else if (kv.length == 1) {
- map.put(kv[0], "");
- }
- }
- return map;
- }
- @Override
- public Response<String> getAccessToken(String code) {
- String url = String.format(ACCESS_TOKEN_URL, API_KEY, API_SECRET, code, CALLBACK_URL, GITHUB_STATE);
- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
- URI uri = builder.build().encode().toUri();
- String resp;
- try {
- resp = getRestTemplate().getForObject(uri, String.class);
- } catch (Exception e) {
- log.error("Github获得access_token失败,code不正确或者已过期, cause:{}", e);
- return Response.no("code无效");
- }
- if (resp != null && resp.contains("access_token")) {
- Map<String, String> map = getParam(resp);
- String access_token = map.get("access_token");
- return Response.yes(access_token);
- }
- log.error("GitHub登录失败,code不正确或者已过期:");
- return Response.no("code无效");
- }
- @Override
- public Response<String> getOpenId(String accessToken) {
- String url = String.format(USER_INFO_URL, accessToken);
- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
- URI uri = builder.build().encode().toUri();
- String resp;
- try {
- resp = getRestTemplate().getForObject(uri, String.class);
- } catch (Exception e) {
- log.error("GitHub获得OpenId失败,access_token无效, cause:{}", e);
- return Response.no("access_token无效!");
- }
- if (resp != null && resp.contains("id")) {
- JSONObject data = JSONObject.parseObject(resp);
- String openid = data.getString("id");
- return Response.yes(openid);
- }
- return Response.no("access_token无效!");
- }
- @Override
- public Response<String> refreshToken(String code) {
- return null;
- }
- @Override
- public Response<String> getAuthorizationUrl() {
- String url = String.format(AUTHORIZE_URL, API_KEY, CALLBACK_URL, GITHUB_STATE);
- return Response.yes(url);
- }
- @Override
- public Response<BindUserDTO> getUserInfo(String accessToken, String openId) {
- String url = String.format(USER_INFO_URL, accessToken);
- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
- URI uri = builder.build().encode().toUri();
- String resp;
- try {
- resp = getRestTemplate().getForObject(uri, String.class);
- } catch (Exception e) {
- log.error("GitHub获得用户信息失败,access_token无效, cause:{}", e);
- return Response.no("access_token无效!");
- }
- if (resp != null && resp.contains("id")) {
- JSONObject data = JSONObject.parseObject(resp);
- BindUserDTO result = new BindUserDTO();
- result.setOpenId(data.getString("id"));
- result.setAvatar(data.getString("avatar_url"));
- result.setNickname(data.getString("name"));
- return Response.yes(result);
- }
- return Response.no("access_token无效!");
- }
- }
5、Controller,根据项目不同,自己修改
这里把qq登录和github登录,未登录之前绑定,登录后再后台绑定融合到了一起。
- package com.liuyanzhao.sens.web.controller.front;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.extra.servlet.ServletUtil;
- import com.liuyanzhao.sens.entity.Log;
- import com.liuyanzhao.sens.entity.ThirdAppBind;
- import com.liuyanzhao.sens.entity.User;
- import com.liuyanzhao.sens.model.dto.LogsRecord;
- import com.liuyanzhao.sens.model.dto.SensConst;
- import com.liuyanzhao.sens.model.enums.BindTypeEnum;
- import com.liuyanzhao.sens.service.*;
- import com.liuyanzhao.sens.utils.Response;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import javax.servlet.http.HttpServletRequest;
- /**
- * @author 言曌
- * @date 2018/5/9 下午2:59
- */
- @Controller
- @Slf4j
- public class AuthController {
- @Autowired
- private GithubAuthService githubAuthService;
- @Autowired
- private UserService userService;
- @Autowired
- private ThirdAppBindService thirdAppBindService;
- @Autowired
- private LogService logService;
- /**
- * 第三方授权后会回调此方法,并将code传过来
- *
- * @param code code
- * @return
- */
- @GetMapping("/oauth/github/callback")
- public String oauthByGitHub(@RequestParam(value = "code") String code,
- HttpServletRequest request) {
- Response<String> tokenResponse = githubAuthService.getAccessToken(code);
- if (tokenResponse.isSuccess()) {
- Response<String> openidResponse = githubAuthService.getOpenId(tokenResponse.getData());
- if (openidResponse.isSuccess()) {
- //根据openId去找关联的用户
- ThirdAppBind bind = thirdAppBindService.findByAppTypeAndOpenId(BindTypeEnum.GITHUB.getValue(), openidResponse.getData());
- if (bind != null && bind.getUserId() != null) {
- //执行Login操作
- User user = userService.findByUserId(bind.getUserId());
- if (user != null) {
- request.getSession().setAttribute(SensConst.USER_SESSION_KEY, user);
- logService.saveByLog(new Log(LogsRecord.LOGIN, LogsRecord.LOGIN_SUCCESS+"(GitHub登录)", ServletUtil.getClientIP(request), DateUtil.date()));
- log.info("用户[{}]登录成功(登录)。", user.getUserDisplayName());
- return "redirect:/admin";
- }
- }
- }
- }
- return "redirect:/admin/login";
- }
- }
您可以选择一种方式赞助本站
支付宝扫一扫赞助
微信钱包扫描赞助
赏