当前位置:   article > 正文

手摸手接入Github实现Oauth2第三方登录_github oauth2登录

github oauth2登录

前言

最近在学习oauth2授权登录流程,oauth2简单来说就是接入第三方应用(qq、微信、github、gitee等),不用在本站登录,而是去请求第三方应用的用户信息完成登录。

下面就一起来看一下如何接入github实现第三方登录

前置操作

首先,我们需要在github中添加OAuth App,登录你的github(如果还有无法登录github的,请在评论区留言或私信我)

点击注册

如果你成功执行到这里,那么就恭喜你已经把前置工作完成了

实战演练

下面就以我个人的博客代码为例,和大家一起实现github第三方登录

博客地址:www.yuuu.online,

项目地址:蔚泽华 (yuuu-zehua) - Gitee.com

业务流程

我这里先说一下我项目的业务流程:用户第一次使用github登录,那么我会让其与我的网站进行绑定(邮箱);如果是非第一次使用,那么就直接登录

话不多说,直接进入!

业务代码

因为我的项目是前后端分离的,所以一切请求都要过一遍前端代理,因此我写了一个Loading页,这也是我添加的github回调地址。

  1. // Loading页的执行方法
  2. oauth() {
  3. const urlParams = new URLSearchParams(window.location.search);
  4. const code = urlParams.get('code');
  5. const state = urlParams.get('state');
  6. if (code && code != '') {
  7. request.post('user/oauth?code=' + code + '&state=' + state).then(response => {
  8. console.log(response);
  9. if(response === undefined){
  10. this.$router.push({ path: '/Login?login=9' });
  11. }else{
  12. setToken(response.token)
  13. localStorage.setItem("userInfo", JSON.stringify(response.userInfo))
  14. this.$router.push({ path: '/' });
  15. }
  16. })
  17. }
  18. },

在这里会根据后端的返回去判断是第一次登录还是非第一次登录,下面是后端接口实现

  1. public ResponseResult oauth(String code) {
  2. LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
  3. BlogUserLoginVo blogUserLoginVo;
  4. try {
  5. String oauthToken = getOauthToken(code);
  6. GithubUser githubUser = getUserInfoWithGithub(oauthToken);
  7. queryWrapper.eq(User::getGithubId, githubUser.getId());
  8. User one = getOne(queryWrapper);
  9. if (one != null) {
  10. // 已经绑定过用户信息,直接登录
  11. blogUserLoginVo = getBlogUserLoginVo(one);
  12. return ResponseResult.okResult(blogUserLoginVo);
  13. } else {
  14. redisCache.setCacheObject(code,githubUser);//注意这里
  15. Map<Integer,String> map = new HashMap<>();
  16. map.put(999, "未绑定");
  17. return ResponseResult.okResult(map);
  18. }
  19. } catch (IOException e) {
  20. throw new SystemException(AppHttpCodeEnum.SYSTEM_ERROR);
  21. }
  22. }
  23. private BlogUserLoginVo getBlogUserLoginVo(User user) {
  24. // 获取userId,生成token
  25. String jwt = JwtUtil.createJWT(user.getId().toString());
  26. // 把用户信息存入redis
  27. LoginUser loginUser = new LoginUser();
  28. loginUser.setUser(user);
  29. loginUser.setPermissions(null);
  30. redisCache.setCacheObject(SystemConstants.BLOG_LOGIN_KEY + user.getId(), loginUser, 1800, TimeUnit.SECONDS);
  31. // 把token和userInfo封装返回
  32. // 把user转换为userInfo
  33. UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
  34. BlogUserLoginVo blogUserLoginVo = new BlogUserLoginVo(jwt, userInfoVo);
  35. return blogUserLoginVo;
  36. }
  37. private String getOauthToken(String code) throws IOException {
  38. String url = "https://github.com/login/oauth/access_token";
  39. HttpHeaders headers = new HttpHeaders();
  40. headers.setContentType(MediaType.APPLICATION_JSON);
  41. headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
  42. String requestBody = String.format("{\"client_id\":\"%s\",\"client_secret\":\"%s\",\"code\":\"%s\"}",
  43. clientId, clientSecret, code);
  44. HttpEntity<String> request = new HttpEntity<>(requestBody, headers);
  45. RestTemplate restTemplate = new RestTemplate();
  46. ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
  47. if (response.getStatusCode().is2xxSuccessful()) {
  48. // 解析JSON响应,获取access_token字段
  49. String jsonString = JSON.toJSONString(response);
  50. JSONObject jsonObject = JSON.parseObject(jsonString);
  51. JSONObject body = jsonObject.getJSONObject("body");
  52. Object token = body.get("access_token");
  53. return token.toString();
  54. } else {
  55. throw new SystemException(AppHttpCodeEnum.SYSTEM_ERROR);
  56. }
  57. }
  58. private GithubUser getUserInfoWithGithub(String oauthToken) throws IOException {
  59. String url = "https://api.github.com/user";
  60. HttpHeaders headers = new HttpHeaders();
  61. headers.set("Authorization", "Bearer " + oauthToken);
  62. headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
  63. HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
  64. RestTemplate restTemplate = new RestTemplate();
  65. ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
  66. if (responseEntity.getStatusCode().is2xxSuccessful()) {
  67. String body = responseEntity.getBody();
  68. cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(body);
  69. GithubUser githubUser = jsonObject.toBean(GithubUser.class);
  70. return githubUser;
  71. } else {
  72. throw new SystemException(AppHttpCodeEnum.SYSTEM_ERROR);
  73. }
  74. }

相关实体类,其实内容有很多,但是在我项目里我只抽出了其中返回的几个字段

  1. public class GithubUser {
  2. private String login;
  3. private Integer id;
  4. private String nodeId;
  5. private String avatarUrl;
  6. }

到这里,如果用户已经绑定过邮箱信息,那么就可以直接登录主页。如果用户没有绑定,就会跳转到绑定页。

这是我的绑定页

绑定的代码也和前面的差不多

前端代码

  1. oauthWithGithub() {
  2. const urlParams = new URLSearchParams(window.location.search);
  3. const code = urlParams.get('code');
  4. const state = urlParams.get('state');
  5. if (!this.emailCaptchaUpdate) {
  6. this.$message.error('验证码不能为空');
  7. } else {
  8. request.post('/user/oauth/github?code='+code, {
  9. emailPassword: this.emailPassword,
  10. confirmNewPassword: this.confirmNewPassword,
  11. emailCaptchaUpdate: this.emailCaptchaUpdate,
  12. })
  13. .then((response) => {
  14. console.log(response)
  15. this.$alert('绑定成功', '', {
  16. confirmButtonText: '确定'
  17. })
  18. this.isButtonDisabled2 = false;
  19. this.emailPassword = '';
  20. this.emailCaptchaUpdate = '';
  21. setToken(response.token)
  22. localStorage.setItem("userInfo", JSON.stringify(response.userInfo))
  23. this.$router.push({ path: '/' });
  24. })
  25. .catch((error) => {
  26. console.log(error);
  27. });
  28. }
  29. },

后端接口

  1. public ResponseResult oauthGithub(ChangePassword user,String code) {
  2. String email = user.getEmailPassword();
  3. String captcha = user.getEmailCaptchaUpdate();
  4. if (StrUtil.isBlank(email)) {
  5. throw new SystemException(AppHttpCodeEnum.EMAIL_ISNULL);
  6. }
  7. if (StrUtil.isBlank(captcha)) {
  8. throw new SystemException(AppHttpCodeEnum.CAPTCHA_ISNULL);
  9. }
  10. LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
  11. queryWrapper.eq(User::getEmail, email);
  12. String cacheCaptcha = redisCache.getCacheObject(SystemConstants.MAIL_KEY + captcha);
  13. if (!captcha.equals(cacheCaptcha)) {
  14. return ResponseResult.errorResult(601, "请再检查一下验证码~");
  15. } else {
  16. redisCache.deleteObject(SystemConstants.MAIL_KEY + captcha);
  17. }
  18. User existingUser = getOne(queryWrapper);
  19. BlogUserLoginVo blogUserLoginVo;
  20. // 还记得上面让大家注意的地方吗?如果忘记了速速回滚查看!
  21. // 因为github的code只能用一次,本来想使用ThreadLocal,但是实现起来总报错,就直接用
  22. // redis存储
  23. GithubUser githubUser = redisCache.getCacheObject(code);
  24. if (existingUser == null) {
  25. // 新增
  26. User userByGithub = new User();
  27. userByGithub.setGithubId(githubUser.getId());
  28. userByGithub.setAvatar(githubUser.getAvatarUrl());
  29. userByGithub.setEmail(user.getEmailPassword());
  30. userByGithub.setPassword(passwordEncoder.encode(SystemConstants.DEFAULT_PASSWORD));
  31. userByGithub.setNickName(githubUser.getNodeId());
  32. userByGithub.setUserName(githubUser.getLogin());
  33. save(userByGithub);
  34. // 用户登录
  35. blogUserLoginVo = getBlogUserLoginVo(userByGithub);
  36. } else {
  37. // 修改
  38. existingUser.setGithubId(githubUser.getId());
  39. updateById(existingUser);
  40. // 用户登录
  41. blogUserLoginVo = getBlogUserLoginVo(existingUser);
  42. }
  43. // 用完就及时删除,避免资源浪费
  44. redisCache.deleteObject(code);
  45. return ResponseResult.okResult(blogUserLoginVo);
  46. }

到这里,就实现了全部的功能,大家可以去尽情的尝试了!

总结

Oauth2实现的原理就是拿code去换第三方的token,然后再用token去获取用户信息,看起来很容易,但是实现起来有点麻烦,其实也不是难,就是麻烦,你需要去看每个厂商的api文档,每一个还都不一样,就比如github和gitee。

我想大家在实现时也会常常出现接口超时,没有办法,我的代码在本地不会超时,但是部署在华为云服务器上就反复报接口超时,后面我也会进行优化。谢谢大家观看。

后续

后面也是成功解决了github超时的问题,给大家说一下方法。

我是自己在Cloudflare开了一个代理,把自己的域名代理到了github
如果有需要我的代理域名的小伙伴可以私信我。如果有需要出教程的也可以在评论区留言,需要的人多了就继续手摸手实现github代理。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/271205
推荐阅读
相关标签
  

闽ICP备14008679号