目录
单点登录是什么?
单点登录(SSO,Single Sign On),是在企业内部多个应用系统(如考勤系统、财务系统、人事系统等)场景下,用户只需要登录一次,就可以访问多个应用系统。
单点登录案例使用方式解析。
我们在A系统进行用户登录了之后,我们通过链接跳转到B系统时,首先带上Code,在我们B系统进行验证Code是否存在。
B系统验证Code是否存在的代码
示例代码
@ApiOperation(value = "单点登录接口")
@GetMapping("onePointLogin")
public ResultVo<String> onePointLogin(@RequestParam String code,
HttpServletResponse response) throws IOException {
if(code == null || "".equals(code)){
log.info("没有获取到code,进行重定向获取code");
try {
response.sendRedirect(RedirectUrl.LOGIN_URL_NOCODE);
}
catch (Exception e){
e.printStackTrace();
}
return ResultVo.ok("ok");
}
//如果有登录信息code,重定向获取accessToken.
log.info("获取到code,进行重定向获取accessToken");
try {
Map<String,Object> params = new HashMap<>();
EasyOkHttpUtil easyOkHttpUtil = bdpUtil.callGet(RedirectUrl.LOGIN_URL_CODE+code, params);
log.info("调用三方接口返回的数据为: " +easyOkHttpUtil );
//获取具体的返回参数
log.info("得到的access_token为: "+easyOkHttpUtil.getAccess_token() + " expires_in: " + easyOkHttpUtil.getExpires_in());
/**
* 如果当前token已过期,则重新进行获取token
*/
if(easyOkHttpUtil.getExpires_in() <=0){
log.info("当前获得的token已过期,重新获取");
try {
//因为这里我不知道code,所以重定向到了获取code的接口,按理说是直接重定向到获取token的接口。
response.sendRedirect(RedirectUrl.LOGIN_URL_NOCODE);
}
catch (Exception ex){
ex.printStackTrace();
}
ResultVo.ok("token已过期,重新获取token");
}
(1) 如果Code不存在,那么重定向到认证系统(A系统提供的获取code的接口)获取Code。重定向的地址我们会提供它重新调我们当前方法的地址(还是通过B系统提供的当前接口,来继续验证Code)。
public static final String LOGIN_URL_NOCODE =
"https://qzx.zh/sso-api/oauth/authorize?client_id=qljq&response_type=code&redirect_uri=http://qzx.zh/qljq/onePointLogin&scope=read";
上面的redirect_uri就是B系统的验证Code的接口
(2) 如果Code存在那么,我们就进行获取当前在A系统登录的token。
Map<String,Object> params = new HashMap<>();
EasyOkHttpUtil easyOkHttpUtil = bdpUtil.callGet(RedirectUrl.LOGIN_URL_CODE+code, params);
log.info("调用三方接口返回的数据为: " +easyOkHttpUtil );
//获取具体的返回参数
log.info("得到的access_token为: "+easyOkHttpUtil.getAccess_token() + " expires_in: " + easyOkHttpUtil.getExpires_in());
获取登录用户的token的接口也是A系统提供的接口,我们去调用三方接口获取到token,和过期时间,作为我们B系统的token。
同时还进行判断当前登录用户信息在B系统是否存在,因为需求在B系统需要展示用户信息。首先到我们自己的数据库查看用户信息是否存在,不存在的话就调用A系统提供的获取用户信息接口获取用户信息。
String userCode = userService.onePointLogin(easyOkHttpUtil.getUser(),easyOkHttpUtil.getAccess_token(),easyOkHttpUtil.getExpires_in()); //此时获取到的code是我们系统生成的token
/**如果此时我们的数据库没有用户信息,那么调用三方接口去获取用户信息*/
if(null == userCode){
Map<String,Object> userParams = new HashMap<>();
userParams.put("appKey","e74963fd39000fd695a1fa8f23ff9d8f");
userParams.put("method","user.queryById");
userParams.put("v","1.0");
userParams.put("format", "json");
userParams.put("id",easyOkHttpUtil.getUser());
String sign = SignUtils.sign(userParams, "qljq");
log.info("得到的sign值为: {}",sign);
userParams.put("sign",sign);
String data = bdpUtil.callGetString(RedirectUrl.LOGIN_URL_GETUSER, userParams);
log.info("调用远程接口得到的json数据为: {}",data);
//把当前的用户信息数据插入数据库在
JSONObject jsonObject = JSONObject.parseObject(data);
UserEntity userEntity = UserEntity.builder()
// .id(jsonObject.getString("userId"))
.username(jsonObject.getString("name"))
// .password(jsonObject.getString("password"))
.mobile(jsonObject.getString("phone"))
.email(jsonObject.getString("email"))
.gender(jsonObject.getString("sex"))
.dept(jsonObject.getString("teamName"))
// .deptId(jsonObject.getInteger("deptId"))
// .status(jsonObject.getBoolean("status"))
.url(jsonObject.getString("avatarImg"))
.idNumber(easyOkHttpUtil.getUser())
.build();
userRepository.save(userEntity);
log.info("数据插入成功");
}
//userService.onePointLogin代码
public String onePointLogin(String userId,String accessToken,Integer tokenTime) {
/**
*根据userId,查询当前用户是否存在。
*/
log.info("要查询的userId为:" + userId);
UserTmpEntity userEntity = userTmpRepository.findByIdNumber(userId);
log.info("查询的结果为:" + userEntity);
if(userEntity == null){
return null;
}
//只要用户存在,就是登录成功
// String random = userEntity.getUsername() + UuidUtil.random();
loginService.cacheUserInfo2(accessToken,tokenTime, userEntity);
return accessToken;
}
B系统提供的进行用户登录的代码
获取到token和用户信息之后,我们就进行跳转到B系统的页面,因为当前登录页面是B系统提供的。
完整代码为:
public ResultVo<String> onePointLogin(@RequestParam String code,
HttpServletResponse response) throws IOException {
if(code == null || "".equals(code)){
log.info("没有获取到code,进行重定向获取code");
try {
response.sendRedirect(RedirectUrl.LOGIN_URL_NOCODE);
}
catch (Exception e){
e.printStackTrace();
}
return ResultVo.ok("ok");
}
//如果有登录信息code,重定向获取accessToken.
log.info("获取到code,进行重定向获取accessToken");
try {
Map<String,Object> params = new HashMap<>();
EasyOkHttpUtil easyOkHttpUtil = bdpUtil.callGet(RedirectUrl.LOGIN_URL_CODE+code, params);
log.info("调用三方接口返回的数据为: " +easyOkHttpUtil );
//获取具体的返回参数
log.info("得到的access_token为: "+easyOkHttpUtil.getAccess_token() + " expires_in: " + easyOkHttpUtil.getExpires_in());
/**
* 如果当前token已过期,则重新进行获取token
*/
if(easyOkHttpUtil.getExpires_in() <=0){
log.info("当前获得的token已过期,重新获取");
try {
//因为这里我不知道code,所以重定向到了获取code的接口,按理说是直接重定向到获取token的接口。
response.sendRedirect(RedirectUrl.LOGIN_URL_NOCODE);
}
catch (Exception ex){
ex.printStackTrace();
}
ResultVo.ok("token已过期,重新获取token");
}
String userCode = userService.onePointLogin(easyOkHttpUtil.getUser(),easyOkHttpUtil.getAccess_token(),easyOkHttpUtil.getExpires_in()); //此时获取到的code是我们系统生成的token
/**如果此时我们的数据库没有用户信息,那么调用三方接口去获取用户信息*/
if(null == userCode){
Map<String,Object> userParams = new HashMap<>();
userParams.put("appKey","e74963fd39000fd695a1fa8f23ff9d8f");
userParams.put("method","user.queryById");
userParams.put("v","1.0");
userParams.put("format", "json");
userParams.put("id",easyOkHttpUtil.getUser());
String sign = SignUtils.sign(userParams, "qljq");
log.info("得到的sign值为: {}",sign);
userParams.put("sign",sign);
String data = bdpUtil.callGetString(RedirectUrl.LOGIN_URL_GETUSER, userParams);
log.info("调用远程接口得到的json数据为: {}",data);
//把当前的用户信息数据插入数据库在
JSONObject jsonObject = JSONObject.parseObject(data);
UserEntity userEntity = UserEntity.builder()
// .id(jsonObject.getString("userId"))
.username(jsonObject.getString("name"))
// .password(jsonObject.getString("password"))
.mobile(jsonObject.getString("phone"))
.email(jsonObject.getString("email"))
.gender(jsonObject.getString("sex"))
.dept(jsonObject.getString("teamName"))
// .deptId(jsonObject.getInteger("deptId"))
// .status(jsonObject.getBoolean("status"))
.url(jsonObject.getString("avatarImg"))
.idNumber(easyOkHttpUtil.getUser())
.build();
userRepository.save(userEntity);
log.info("数据插入成功");
}
//重新进行获取用户信息
userCode = userService.onePointLogin(easyOkHttpUtil.getUser(),easyOkHttpUtil.getAccess_token(),easyOkHttpUtil.getExpires_in());
if(null == userCode){
try {
response.sendRedirect(RedirectUrl.LOGIN_URL_NOCODE);
}
catch (Exception e){
e.printStackTrace();
}
return ResultVo.ok("ok");
}
log.info("获取到用户信息的code为:" + userCode);
Cookie cookie = new Cookie(Constant.HTTP_USER_HEADER, userCode);
cookie.setMaxAge(easyOkHttpUtil.getExpires_in());
response.addCookie(cookie);
log.info("登录成功");
response.sendRedirect("http://qzx.zh.sh/qljq/home");
// response.sendRedirect(RedirectUrl.LOGIN_URL_CODE + code);
}
catch (Exception e){
e.printStackTrace();
response.sendRedirect(RedirectUrl.LOGIN_URL_NOCODE);
}
return ResultVo.ok("ok");
}
在上面示例代码,通过传递过来的用户信息,和在A系统登录获得的token,和token的过期时间,进行在B系统用户的登录。
在B系统登录完成之后,重定向跳转到B系统的首页,进行操作B系统。