文章

自己实现对接OAuth2单点登录系统

自己实现对接OAuth2单点登录系统

OAuth2单点登录对接的时序图如下所示:

31-1

1.OAuth2单点登录处理流程

前端页面处理流程步骤为:

图上的1,3,6,12红色字体的流程步骤。

前端需要相应代码来处理。


后端服务处理流程步骤:

图上2,7,10,11蓝色字体的流程步骤。

后端需要相应代码来处理。


OAuth2处理流程步骤:

图上4,5,8,9紫色字体的流程步骤。单点登录系统自己处理。


1.1.自己要实现对接OAuth2单点登录系统,就需要按上图所示实现对应的前端和后端代码改造。

图上第7点后端需要实现的示例代码如下:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
@PostMapping(value = "/ssoLogin")
public User ssoLogin(@RequestParam String code) throws JsonProcessingException {
  // 通过code获取access token
  String accessToken = oauth2ValidateCode(code);
  // 通过access token获取登录用户账号
  String userAccount = oauth2GetUser(accessToken);
 
  // 从OAuth2单点登录系统验证成功后需要再自动登录本系统
  User user = autoLogin(userAccount);
  return user;
}
 
/**
 * 通过code获取access token
 *
 * @param code
 * @return
 * @throws JsonProcessingException
 */
public String oauth2ValidateCode(String code) throws JsonProcessingException {
  String result = null;
  RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
  RestTemplate restTemplate = restTemplateBuilder.build();
  // 接口参数
  MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
  params.add("client_id", client_id);
  params.add("client_secret", client_secret);
  params.add("grant_type", "authorization_code");
  params.add("code", code);
  params.add("redirect_uri", redirect_uri);
 
  // 头部类型
  HttpHeaders headers = new HttpHeaders();
  headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
 
  // http实体
  HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity(params, headers);
  ResponseEntity<String> response = restTemplate.postForEntity(oauth2Url, entity, String.class);
  ObjectMapper jsonMapper = new ObjectMapper();
  String body = response.getBody();
  JsonNode root = jsonMapper.readTree(body);
  // code验证成功就会返回access_token
  JsonNode tokenNode = root.findValue("access_token");
  if (tokenNode != null) {
    String access_token = tokenNode.asText();
    log.debug("#################################### OAuth2 access_token: " + access_token);
    result = access_token;
  } else {
    throw new BusinessException("OAuth2单点登录验证失败:" + body);
  }
  return result;
}
 
/**
 * 通过access token获取登录用户账号
 *
 * @param accessToken
 * @return
 * @throws JsonProcessingException
 */
public String oauth2GetUser(String accessToken) throws JsonProcessingException {
  String result = null;
  RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
  RestTemplate restTemplate = restTemplateBuilder.build();
 
  // 头部类型
  HttpHeaders headers = new HttpHeaders();
  headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
  headers.set("Authorization", "Bearer " + accessToken);
 
  // http实体
  HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity(null, headers);
  ResponseEntity<String> response =
      restTemplate.exchange(userinfoUrl, HttpMethod.GET, entity, String.class);
  ObjectMapper jsonMapper = new ObjectMapper();
  String body = response.getBody();
  JsonNode root = jsonMapper.readTree(body);
  // 接口会返回登录用户相关信息
  JsonNode userNode = root.findValue("user");
  if (userNode != null) {
    String user = userNode.asText();
    log.debug("#################################### OAuth2 login user: " + user);
    result = user;
  } else {
    throw new BusinessException("OAuth2单点登录获取用户信息失败:" + body);
  }
  return result;
}

注意:

oauth2Url是授权服务器中的code认证API,形如:

http://ip:port/services/authorization/oauth/token

userinfoUrl是资源服务器中的获取登录用户信息的API,形如:

http://ip:port/services/resource/user/getUserInfo


1.2.除了完成对接OAuth2的认证流程,还需要从单点登录系统中定时拉取组织和用户信息到自己系统中

现在OAuth2单点登录系统中的组织和用户信息API只支持查询全量数据。

组织GET API URL示例如下:

http://ip:port/services/admin/api/open/sync/listOrg

请求组织的url返回的信息字段:

字段名称 字段描述 备注
id 唯一标识  
name 组织名称  
type 组织类型:公司、子公司、单位、部门等  
parentId 父组织ID  
sort 排序编号  


用户GET API URL示例如下:

http://ip:port/services/admin/api/open/sync/listUser

请求用户的url返回的信息字段:

字段名称 字段描述 备注
id 唯一标识  
account 账号名  
realName 实名  
orgId 用户组织  


2.单点登出:

第三方系统登出时直接重定向到统一的单点登出URL即可,形如:

http://ip:port/services/authorization/oauth/logout

本文由作者按照 CC BY 4.0 进行授权