【OAuth2】第三方客户端接入——登录流程
苗锦洲
0
27
180
0
OAuth2第三方客户端登录流程
友情提示:此篇文章大约需要阅读 17分
### 登录流程
1. 先注册第三方客户端
2. 安卓端构建OAuth2授权链接
3. 认证服务器重定向到注册时填的redirect_uri
4. 接收code,换取access_token,获取Openid
5. 使用Openid登录IoE模块
6. 根据Openid判断IoE模块是否存在用户
7. 未存在则先使用access_token换取userinfo,拿到email、username,创建Customer、创建User、激活User、创建IoE用户
8. 已存在则直接将access_token和satoken和state传回authorized.html
9. 网页构建打开APP的URI,提前在APP中设置好
10. APP接收state、access_token和satoken
11. APP先判断state是否一致,不一致/access_token或satoken为空则提示登录失败
12. APP使用access_token向OAuth2服务器换取userinfo,保存用户信息
13. APP保存用于访问IoE模块的satoken
14. APP端登录完成
### 后端代码
```java
/*
* MIT License
*
* Copyright (c) 2021 苗锦洲
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @author mjz
* @date 2022/3/22
*/
@Slf4j
@RequiredArgsConstructor
@RestController
public class IoEController implements IIoEApi {
private final OAuth2ClientProperties oAuth2ClientProperties;
private final IIoEUserFacade ioEUserFacade;
private final IoEUserService userService;
private final IOAuth2Api oAuth2Api;
private final OrThingsBoardProperties thingsBoardProperties;
private final OrThingsBoardCustomerService thingsBoardCustomerService;
private final OrThingsBoardUserService thingsBoardUserService;
@Transactional(rollbackFor = BaseException.class)
@Override
public ModelAndView authorized(
@RequestParam(required = false) String code,
@RequestParam(required = false) String state,
// refuse
@RequestParam(required = false) String handle,
// 拒绝消息
@RequestParam(required = false) String msg
) {
log.info("auth-server登录成功回调:{},{}", code, state);
// 1. 用code获取Openid和AccessToken
JSONObject params = new JSONObject();
params.put(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.authorization_code);
params.put(SaOAuth2Consts.Param.code, code);
params.put(SaOAuth2Consts.Param.client_id, oAuth2ClientProperties.getClientId());
params.put(SaOAuth2Consts.Param.client_secret, oAuth2ClientProperties.getClientSecret());
params.put(SaOAuth2Consts.Param.state, state);
// 需要将结果封装
params.put("wrapped", true);
Result<JSONObject> response = Result.parse(
HttpUtil.get(oAuth2ClientProperties.getAuthServerHost() + SaOAuth2Consts.Api.token, params)
);
// code不等于200 代表请求失败
if (response == null) {
throw new BaseException(StatusCode.COMMON_FAIL);
}
if (!response.getSuccess()) {
throw new BaseException(response.getMsg());
}
JSONObject data = response.getData();
log.info("IoE authorized(), 1. AccessToken: {}", data);
final String accessToken = data.getString("access_token");
String openid = data.getString("openid");
// 2. 使用Openid登录IoE
StpUtil.login(openid, Boolean.TRUE);
final String tokenValue = StpUtil.getTokenValue();
log.info("IoE authorized(), 2. login successfully.,\nAccessToken: {}\nOpenid:{}\nSaToken:{}", data, openid, tokenValue);
// 3. 根据openid查询IoE User是否存在,不存在则直接创建
final IoEUserQueryRequest request = new IoEUserQueryRequest();
request.setOpenid(openid);
final Result<IoEUserDTO> ioeUserResult = ioEUserFacade.findByUniqueColumn(request);
IoEUserDTO ioEUserDTO;
if (ioeUserResult.getSuccess()) {
log.info("IoE authorized(), 3. IoEUser already existed, skip create");
ioEUserDTO = ioeUserResult.getData();
} else {
log.info("IoE authorized(), 3.1/5 IoEUser not exist, creating...");
// 获取userinfo
final OAuth2UserinfoRequest oAuth2UserinfoRequest = new OAuth2UserinfoRequest();
oAuth2UserinfoRequest.setAccessToken(accessToken);
final Result<OAuth2UserInfoDTO> userinfoResult = oAuth2Api.userinfo(oAuth2UserinfoRequest);
if (!userinfoResult.getSuccess()) {
throw new BaseException(userinfoResult.getMsg());
}
final OAuth2UserInfoDTO userInfoDTO = userinfoResult.getData();
final SysUserDTO sysUserDTO = userInfoDTO.getUser();
final String defaultUsername = sysUserDTO.getUsername();
// 创建Customer
final String tenantId = thingsBoardProperties.getTenant().getId();
final Customer customer = thingsBoardCustomerService.create(tenantId, null, defaultUsername);
log.info("IoE authorized(), 3.2/5 ThingsBoard Customer created successfully, {}", customer);
// 创建Customer下的User
final String customerId = customer.getId().toString();
final String email = sysUserDTO.getEmail();
final User user = thingsBoardUserService.create(email, defaultUsername, null, tenantId, customerId);
log.info("IoE authorized(), 3.3/5 ThingsBoard User created successfully, {}", user);
// 直接激活User
final String userId = user.getId().toString();
thingsBoardUserService.active(userId);
log.info("IoE authorized(), 3.4/5 ThingsBoard User activation succeeded");
// 创建新IoE用户
final IoEUserSaveRequest ioEUserSaveRequest = new IoEUserSaveRequest();
ioEUserSaveRequest.setOpenid(openid);
ioEUserSaveRequest.setCustomerId(customerId);
ioEUserSaveRequest.setUserId(userId);
ioEUserSaveRequest.setUsername(defaultUsername);
final Result<IoEUserDTO> ioEUserDTOResult = ioEUserFacade.create(ioEUserSaveRequest);
if (!ioEUserDTOResult.getSuccess()) {
throw new BaseException(ioEUserDTOResult.getMsg());
}
ioEUserDTO = ioEUserDTOResult.getData();
log.info("IoE authorized(), 3.5/5 IoEUser successfully created");
}
// 4. 处理完毕,传参到网页进行重定向
Map<String, Object> map = new HashMap<>(3);
map.put(SaOAuth2Consts.Param.access_token, accessToken);
map.put("satoken", tokenValue);
map.put(SaOAuth2Consts.Param.state, state);
log.info("IoE authorized(), 4. Finished, IoEUser: {}", ioEUserDTO);
return new ModelAndView("authorized.html", map);
}
@Override
public Result<IoEUserinfoDTO> userinfo() {
final IoEUserinfoDTO ioEUserinfoDTO = new IoEUserinfoDTO();
final String openid = StpUtil.getLoginIdAsString();
final IoEUserQueryRequest request = new IoEUserQueryRequest();
request.setOpenid(openid);
final Result<IoEUserDTO> ioeUserResult = ioEUserFacade.findByUniqueColumn(request);
if (!ioeUserResult.getSuccess()) {
return Result.fail();
}
ioEUserinfoDTO.setUser(ioeUserResult.getData());
return Result.success(ioEUserinfoDTO);
}
@Override
public Result<JsonNode> thingsboardToken() {
final String openid = StpUtil.getLoginIdAsString();
final Optional<IoEUserDO> optional = userService.findByOpenid(openid);
return optional.map(ioEUserDO -> Result.success(thingsBoardUserService.getToken(ioEUserDO.getUserId()))).orElse(Result.fail());
}
}
```
评论
已自动恢复阅读位置、日/夜间模式参数