这是什么

OAuth 2.0定义了四种授权方式:

  • 授权码模式(Authorization Code)
  • 简化模式(Implicit Grant)
  • 密码模式(Resource Owner Password Credentials Grant)
  • 客户端模式(Client Credentials)

其中授权码模式在应用与应用之间广为使用,比如微信第三方授权登录;其特点是不会涉及到客户端应用的账号密码信息,只需要通过简单的约定好的握手原则就可以拿到第三方用户的资源权限;

OAuth(开放授权)是一个开放标准,是一种约定俗成的规则;

需要什么

  • 客户端(我,即应用程序服务器)
  • 资源所有者(用户)
  • 授权服务器(负责颁发令牌)
  • 资源服务器(接收访问令牌,提供用户资源)

工作流程

举一个Github登录的例子:首先用户被重定向到GitHub的授权页面,证明自己的身份并同意授权后获取一个授权码,然后客户端拿着授权码请求授权服务器获得一个「Access Token」;

之后客户端就可以通过Access Token来与资源服务器进行交互,这个token是长期有效的,也可以服务端自行留存。服务端通过Access Token可以向GitHub获取用户信息;

我们常见的单点登录平台,如Casdoor、logto等等,他们会在保存授权后获得的Access Token,并作为授权服务器提供服务。利用这些平台我们能更方便的管理账号系统,不用再去对不同的第三方平台做不同的授权实

实现

首先,你需要在GitHub上创建一个Application,将我们的应用注册到GitHub

注意这个回调地址,它是用户授权后GitHub重定向用户的地址

注册完成后,将client_idclient_secret配置到SpringBoot应用中:

下面是Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@RestController
@RequestMapping(path = "oauth")
public class OAuthController {

@Value("${github.client.id}")
private String clientId;

@Value("${github.client.secret}")
private String clientSecret;

/**
* 跳转到授权页面
* @param response
* @throws IOException
*/
@GetMapping(path = "github")
public void auth(HttpServletResponse response) throws IOException {
String authorizeUri = "https://github.com/login/oauth/authorize";
String redirectUri = "http://localhost:8080/oauth/redirect";

String url = authorizeUri
+ "?client_id=" + githubClientProperties.getClientId()
+ "&redirect_uri=" + redirectUri;
response.sendRedirect(url);
}

/**
* Github回调接口
* @param requestToken 授权码
* @return
*/
@GetMapping(path = "redirect")
public String handleRedirect(@RequestParam("code") String requestToken, HttpServletResponse response) {
RestTemplate restTemplate = new RestTemplate();

// 获取Token
String tokenUrl = "https://github.com/login/oauth/access_token"
+ "?client_id=" + githubClientProperties.getClientId()
+ "&client_secret=" + githubClientProperties.getClientSecret()
+ "&code=" + requestToken;

AccessTokenResponse tokenResponse = restTemplate.postForObject(tokenUrl, null, AccessTokenResponse.class);
String accessToken = tokenResponse.getAccessToken();

// 获取用户信息
String apiUrl = "https://api.github.com/user";
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
ResponseEntity<String> resp = restTemplate.exchange(apiUrl, HttpMethod.GET, entity, String.class);
String userData = resp.getBody();

// 将token返回给客户端
Cookie githubToken = newCookie("github_token", accessToken, "/", COOKIE_AGE);
response.addCookie(githubToken);

return ResVo.ok(userData);
}

public static Cookie newCookie(String key, String session, String path, int maxAge) {
Cookie cookie = new Cookie(key, session);
cookie.setPath(path);
cookie.setMaxAge(maxAge);
return cookie;
}
}