博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于oauth2.0实现应用的第三方登录
阅读量:4568 次
发布时间:2019-06-08

本文共 10686 字,大约阅读时间需要 35 分钟。

OAuth2

OAuth2所涉及到的对象主要有以下四个:

  • Client 第三方应用,我们的应用就是一个Client
  • Resource Owner 资源所有者,即用户
  • Authorization Server 授权服务器,即提供第三方登录服务的服务器,如Github
  • Resource Server 拥有资源信息的服务器,通常和授权服务器属于同一应用

OAuth2的基本流程为:

  1. 第三方应用请求用户授权。
  2. 用户同意授权,并返回一个凭证(code)
  3. 第三方应用通过第二步的凭证(code)向授权服务器请求授权
  4. 授权服务器验证凭证(code)通过后,同意授权,并返回一个资源访问的凭证(Access Token)。
  5. 第三方应用通过第四步的凭证(Access Token)向资源服务器请求相关资源。
  6. 资源服务器验证凭证(Access Token)通过后,将第三方应用请求的资源返回。

Github对应用开放授权

进入github中的Settings/Developer settings中创建一个应用,表示你的应用会使用github授权。

填写好相关的信息后,填写Authorization callback URL为http://localhost:8080/oauth/github/callback(后面授权会用到),可以得到Client IDClient Secret,结果如下:

o_%e6%90%9c%e7%8b%97%e6%88%aa%e5%9b%be20190403093052.png

github授权第三方应用的过程

  1. 根据 GitHub 登录链接可以回调获得 code

  2. 根据Client ID 、Client Secret 和 code 可获得 token

  3. 根据 token 获得用户信息

必要的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

应用获得用户的信息时,会返回一个唯一的标识,用于唯一标识资源所有者即用户,于是我们可以将此标识与数据库中我们自己的本地用户相关联。

o_%e6%90%9c%e7%8b%97%e6%88%aa%e5%9b%be20190403094851.png

测试

在进行编码之前,我们首先访问上面的几种URL,并分析流程及返回结果。

首先访问https://github.com/login/oauth/authorize?client_id=50d7f61132da7f8574a1&redirect_uri=http://localhost:8080/oauth/github/callback&state=thisisrandomstring

分析:该URL为引导用户对应用授权github信息,参数client_id为该应用创建时的Client ID,redirect_uri为该应用创建时填写的Authorization callback URL,state为随机字符串,它用于防止跨站点请求伪造攻击。访问时结果如下:

o_%e6%90%9c%e7%8b%97%e6%88%aa%e5%9b%be20190403103314.png

响应结果能够理解,然后点击授权按钮,就会自动跳转到http://localhost:8080/oauth/github/callback?code=107b7d2f85201535880c&state=thisisrandomstring,URL为我们填写的回调URL,code参数即为凭证,

state为上一步的随机字符串。

接下来,我们应该获取token,根据github官方文档,我们需要发起一个POST请求,URL为https://github.com/login/oauth/access_token

需要携带的参数如下:

Name Type Description
client_id string Required. The client ID you received from GitHub for your GitHub App.
client_secret string Required. The client secret you received from GitHub for your GitHub App.
code string Required. The code you received as a response to Step 1.
redirect_uri string The URL in your application where users are sent after authorization.
state string The unguessable random string you provided in Step 1.

接下来,我们通过Postman模拟这一个过程,结果如下:

o_%e6%90%9c%e7%8b%97%e6%88%aa%e5%9b%be20190403110226.png

您还可以根据Accept标头接收不同格式的内容:

Accept: application/json{"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"repo,gist", "token_type":"bearer"}Accept: application/xml
bearer
repo,gist
e72e16c7e42f292c6912e7710c838347ae178b4a

嗯,成功获取到了Token无误,接下来该获取用户的信息了。发起GET请求,URL为https://api.github.com/user,携带参数access_token=获取到的token,结果如下,可以获取到用户的基本信息。

o_%e6%90%9c%e7%8b%97%e6%88%aa%e5%9b%be20190403111806.png

编码

  1. 首先需要一个service用来定义oauth的一些方法,如获取token,获取用户信息等。

    package com.yunche.novels.service;import com.yunche.novels.vo.AuthUserVO;import org.springframework.util.MultiValueMap;/** * @author yunche * @date 2019/04/04 */public interface AuthService {    String getToken(MultiValueMap
    params); AuthUserVO getUserInfo(String token); boolean checkIsExistsOpenId(String openId); boolean storeOpenIdByUser(String openId, Integer userId); String getUserNameByOpenId(String openId);}
  2. 接着,使用GitHub来完成具体的service的实现。

    package com.yunche.novels.service.impl;import com.yunche.novels.mapper.AuthForGitHubMapper;import com.yunche.novels.service.AuthService;import com.yunche.novels.util.AuthHelper;import com.yunche.novels.vo.AuthTokenVO;import com.yunche.novels.vo.AuthUserVO;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.util.LinkedMultiValueMap;import org.springframework.util.MultiValueMap;import java.sql.Timestamp;import java.util.Date;import java.util.HashMap;import java.util.Map;/** * @author yunche * @date 2019/04/04 */@Servicepublic class GitHubAuthServiceImpl implements AuthService {    @Autowired    private AuthForGitHubMapper gitHubMapper;    private static final String GET_TOKEN_URL = "https://github.com/login/oauth/access_token";    private static final String GET_USER_URL = "https://api.github.com/user";    private static final String CLIENT_ID = "50d7f61132da7f8574a1";    private static final String CLIENT_SECRET = "6779d154cfc44115e1f3607c0000085c5c1cf178";    private static final String REDIRECT_URI = "http://localhost:8080/oauth/github/callback";    @Override    public String getToken(MultiValueMap
    params) { params.add("client_id", CLIENT_ID); params.add("client_secret", CLIENT_SECRET); params.add("redirect_uri", REDIRECT_URI); AuthTokenVO authTokenVO = AuthHelper.sendPostGetToken(GET_TOKEN_URL, params); String token = authTokenVO.getAccess_token(); return token; } @Override public AuthUserVO getUserInfo(String token) { Map
    map = new HashMap<>(); map.put("access_token", token); return AuthHelper.sendGetToUser(GET_USER_URL, map); } @Override public boolean checkIsExistsOpenId(String openId) { return gitHubMapper.checkIsExists(openId) > 0; } @Override public boolean storeOpenIdByUser(String openId, Integer userId) { Date date = new Date(); Timestamp timeStamp = new Timestamp(date.getTime()); return gitHubMapper.storeOpenIdByUser(openId, userId, timeStamp) > 0; } @Override public String getUserNameByOpenId(String openId) { return gitHubMapper.getUserNameByOpenId(openId); }}
  3. 将需要获取的token和用户信息的json封装成对象。

    package com.yunche.novels.vo;/** * @author yunche * @date 2019/04/04 */public class AuthTokenVO {    private String access_token;    private String token_type;    private String scope;    public String getAccess_token() {        return access_token;    }    public void setAccess_token(String access_token) {        this.access_token = access_token;    }    public String getToken_type() {        return token_type;    }    public void setToken_type(String token_type) {        this.token_type = token_type;    }    public String getScope() {        return scope;    }    public void setScope(String scope) {        this.scope = scope;    }    public AuthTokenVO() {    }    public AuthTokenVO(String access_token, String token_type, String scope) {        this.access_token = access_token;        this.token_type = token_type;        this.scope = scope;    }}
    package com.yunche.novels.vo;/** * @author yunche * @date 2019/04/04 */public class AuthUserVO {    /**     * 用户第三方应用名     */    private String login;    /**     * 用户第三方唯一标识     */    private String id;    /**     * 用户第三方头像     */    private String avatar_url;    public String getLogin() {        return login;    }    public void setLogin(String login) {        this.login = login;    }    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }    public String getAvatar_url() {        return avatar_url;    }    public void setAvatar_url(String avatar_url) {        this.avatar_url = avatar_url;    }}
  4. mapper类操作数据库。

    package com.yunche.novels.mapper;import org.apache.ibatis.annotations.*;import java.util.Date;/** * @author yunche * @date 2019/04/05 */@Mapperpublic interface AuthForGitHubMapper {    /**     * 检查该openId是否已经注册过     * @param openId     * @return     */    @Select("SELECT COUNT(*) FROM oauth_detail WHERE open_id=#{openId} and app_type='github'")    Integer checkIsExists(String openId);    /**     * 存储该OpenId     * @param openId     * @param userId     * @return     */    @Insert("INSERT INTO oauth_detail(open_id, app_type, user_id, status, create_time) VALUES(#{openId},'github',#{userId},1,#{createTime})")    Integer storeOpenIdByUser(@Param(value = "openId") String openId, @Param(value = "userId") Integer userId, @Param(value = "createTime") Date createTime);    @Select("SELECT user_name FROM user, oauth_detail WHERE user_id=user.id AND open_id = #{openId}")    String getUserNameByOpenId(String openId);}
    package com.yunche.novels.mapper;import com.yunche.novels.bean.User;import org.apache.ibatis.annotations.*;/** * @author yunche * @date 2019/04/05 */@Mapperpublic interface UserMapper {    @Insert("INSERT INTO user(user_name, password) VALUES(#{userName}, #{password}) ")    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")    Integer storeUser(User user);    @Select("SELECT COUNT(*) FROM user where user_name=#{name}")    Integer checkUserNameIsExists(String name);}
  5. Controller类。

    package com.yunche.novels.controller;import com.yunche.novels.bean.User;import com.yunche.novels.service.UserService;import com.yunche.novels.service.impl.GitHubAuthServiceImpl;import com.yunche.novels.util.MD5Utils;import com.yunche.novels.util.StringHelper;import com.yunche.novels.vo.AuthUserVO;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.util.LinkedMultiValueMap;import org.springframework.util.MultiValueMap;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpSession;/** * @author yunche * @date 2019/04/04 */@Controllerpublic class AuthController {    @Autowired    private GitHubAuthServiceImpl authService;    @Autowired    private UserService userService;    @GetMapping("/oauth/github/callback")    public String authorizeForGitHub(@RequestParam("code") String code, @RequestParam("state") String state, HttpSession session) {        MultiValueMap
    map = new LinkedMultiValueMap<>(); map.add("code", code); map.add("state", state); String token = authService.getToken(map); //获取用户在第三方的信息 AuthUserVO userVO = authService.getUserInfo(token); String openId = userVO.getId(); //注册该openId if(!authService.checkIsExistsOpenId(openId)) { User u = new User(); String userName = userVO.getLogin(); //确保用户的用户名唯一 while (userService.IsExistsName(userName)) { userName += StringHelper.getRandomString(3); } u.setUserName(userName); //生成一个随机的一定长度的字符串并使用MD5加密,由于第三方的密码不可用,故随机。 u.setPassword(MD5Utils.getMD5(StringHelper.getRandomString(16))); //注册用户 if(userService.insertUser(u)) { //将本地用户与OpenId相关联 if(authService.storeOpenIdByUser(openId, u.getId())) { //存储用户session session.setAttribute("user", u.getUserName()); } } } else { session.setAttribute("user", authService.getUserNameByOpenId(openId)); } // 重定向到之前需要授权的页面 return "redirect:" + state; }}

参考资料

转载于:https://www.cnblogs.com/yunche/p/10695430.html

你可能感兴趣的文章
centos7 mysql命令_Centos7中mysql安装以及命令
查看>>
mysql8创建不了用户_mysql8创建用户
查看>>
mysql数据库 q_mySQL数据库 - osc_q4xkkmlj的个人空间 - OSCHINA - 中文开源技术交流社区...
查看>>
mysql yum 卸载命令_Linux下MySQL5.7.18 yum方式从卸载到安装
查看>>
mysql use procedure bodies_Debug core file with no symbols
查看>>
debian安vs_如何在Debian 10 Linux上安装和使用Docker
查看>>
jdbc mysql 5.05_JDBC 连接 MySQL 时碰到的小坑
查看>>
rancher部署mysql怎么挂在卷轴_Rancher部署mysql8
查看>>
java_home没有定义_“错误:JAVA_HOME没有正确定义.”在构建Jikes rvm
查看>>
python canvas画移动物体_tkinter – 用于画布对象python的动画移动的方法
查看>>
java 连接 rac_JAVA 连接 ORACLE RAC 字符串
查看>>
java面试题 网络编程_java面试题《三、网络编程》
查看>>
java布尔矩阵程序_Java编程学习摘要(2)语法基础
查看>>
java no wait_即使队列在activemq中不为空,JMS实现中的receiveNoWait也返回null
查看>>
java定义player类_简易扑克牌游戏 定义了Constants、Main、Player、Poker四个类
查看>>
java方法重载例题_Java方法重载实现原理及代码实例
查看>>
java 字符串 包含 次数_用JAVA写查询一个字符串中是否包含另外一个字符串以及出现的次数...
查看>>
java jvm arg_java – Ant,jvmarg,系统属性和引号
查看>>
karp算法Java_Java – 具有Held和Karp算法的旅行推销员
查看>>
Session共享问题---理论
查看>>