1 用户名和密码登录获取token
1.1 获取验证码
(1)生成图形验证码,将图形验证码保存到redis(key,value)
public Result<AcCaptchaOutDTO> generateCaptcha() throws ServiceException {
try {
AcCaptchaDTO acCaptchaDTO = this.acCaptchaProcessor.generate();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ImageIO.write(acCaptchaDTO.getImage(), "png", bout);
bout.close();
String uuid = AcToolUtils.getUUID();
this.acCacheProvider.getAcCacheDao().set("VerifyCaptcha:" + uuid, acCaptchaDTO.getCode(), this.acCaptchaProperties.getValidSecond());
AcCaptchaOutDTO acCaptchaOutDTO = new AcCaptchaOutDTO();
acCaptchaOutDTO.setCaptchaKey(uuid);
acCaptchaOutDTO.setImgByteArray(bout.toByteArray());
return Result.success(acCaptchaOutDTO);
} catch (Exception var5) {
this.logger.error("生成图形验证码失败", var5);
throw new ServiceException("56000081", this.getMessage("56000081"));
}
}
(2)将验证码对应的key只保存到cookie中
public Result<AcCaptchaOutDTO> captcha(HttpServletResponse response) {
try {
Result<AcCaptchaOutDTO> captchaResult = acCaptchaService.generateCaptcha();
Cookie cookie = new Cookie("VerificationCode", captchaResult.getData().getCaptchaKey());
cookie.setMaxAge(1800);
cookie.setSecure(cwosPlatformProperties.getHttpsEnable());
cookie.setPath("/" + rootPath + "/portal/account");
response.addCookie(cookie);
return Result.success(captchaResult.getData());
} catch (ServiceException e) {
return Result.fail(e.getCode(), e.getMessage());
} catch (Exception e) {
logger.error("获取验证码失败,原因:", e);
return Result.fail(AuthRespCodeEnum.RESPONSE_GET_CAPTCHA_FAIL.getCode(),
AuthRespCodeEnum.RESPONSE_GET_CAPTCHA_FAIL.getMessage());
}
}
1.2 携带验证码登录
(1)验证码的key赋值给request的参数
private boolean setCaptchaKey(LoginParam param, HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
String srcUuid = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
if (CAPTCHA_COOKIE_NAME.equals(cookie.getName())) {
srcUuid = cookie.getValue();
break;
}
}
}
if (StringUtils.isBlank(srcUuid)) {
return true;
}
param.setCaptchaKey(srcUuid);
return false;
}
(2) 验证码校验
if (!StringUtils.isBlank(captchaKey) && !StringUtils.isBlank(verifyCode)) {
String captcha = this.acCacheProvider.getAcCacheDao().get("VerifyCaptcha:" + captchaKey);
this.acCacheProvider.getAcCacheDao().remove("VerifyCaptcha:" + captchaKey);
if (!StringUtils.isBlank(captcha) && captcha.equalsIgnoreCase(verifyCode)) {
UserResultDTO userResult = null;
boolean needLdap = this.needLdapLogin(loginParam);
if (needLdap) {
userResult = this.ldapLogin(loginParam);
if (null == userResult) {
this.logger.error("ldap登录返回信息为空");
throw new ServiceException("56000036", this.getMessage("56000036"));
}
}
}
(3)验证用户名和密码
AcAccount acAccount = this.acAccountMapper.login(loginParam.getLoginName(), businessId);
if (acAccount == null) {
if (!needLdap) {
throw new ServiceException("56000043", this.getMessage("56000043"));
}
acAccount = new AcAccount();
acAccount.setStatus(AcStatusEnum.STATUS_VALID.getCode());
acAccount.setId(AcToolUtils.getUUID());
acAccount.setBusinessId("");
acAccount.setLoginName(userResult.getAccountName());
acAccount.setEmail(userResult.getEmail());
acAccount.setPassword(loginParam.getPassword());
acAccount = AcEncryptUtils.encryptPassword(acAccount);
this.acAccountMapper.insertSelective(acAccount);
}
if (!this.ldapProperties.isLogin()) {
String pwdEncrypt = AcEncryptUtils.encryptPassword(loginParam.getPassword(), acAccount.getSalt());
if (!acAccount.getPassword().equals(pwdEncrypt)) {
throw new ServiceException("56000045", this.getMessage("56000045"));
}
}
acAccount.setUserResult(userResult);
return acAccount;
(4)生成token 并保存到redis和mysql
public String generate(String key, String claims, long accessTokenExp, long refreshTokenExp) {
TokenInfoDO tokenInfoDO = new TokenInfoDO();
JSONObject jsonObject = JSON.parseObject(claims);
if (jsonObject.containsKey("deviceId")) {
tokenInfoDO = (TokenInfoDO)JSON.parseObject(claims, TokenInfoDO.class);
JSONObject claimsObject = new JSONObject();
claimsObject.put("deviceId", jsonObject.get("deviceId"));
claimsObject.put("apiKey", jsonObject.get("apiKey"));
claimsObject.put("generateTime", System.currentTimeMillis());
claims = claimsObject.toJSONString();
}
String accessToken = this.createToken(claims, 1);
String refreshToken = this.createToken(claims, 2);
tokenInfoDO.setAccessToken(accessToken);
tokenInfoDO.setRefreshToken(refreshToken);
tokenInfoDO.setAccessTokenExp(accessTokenExp);
tokenInfoDO.setRefreshTokenExp(refreshTokenExp);
tokenInfoDO.setRefreshed(false);
String result = JSON.toJSONString(tokenInfoDO);
String tokenKey = String.format("JWTToken:%s", key);
String refreshTokenKey = String.format("JWTRefreshToken:%s", key);
return this.tokenDao.set(tokenKey, result, refreshTokenExp) && this.tokenDao.set(refreshTokenKey, result, refreshTokenExp) ? result : null;
}
2 携带token路由转发访问资源
2.1 nginx配置
将访问服务的路径路由到网关
2.2 检测服务是否存在
网关通过服务名称去查找服务,因此,先要判断服务是否存在。
/**
* 获取服务配置
* @param requestUrl
* @param requestContext
* @return
*/
protected ServiceConfig getServiceConfig(String requestUrl, RequestContext requestContext) {
// 请求地址为空
if (StringUtils.isEmpty(requestUrl)) {
return null;
}
if (requestUrl.startsWith("/zuul/")) {
requestUrl = requestUrl.replace("/zuul/", "");
}
int start = 0;
if (requestUrl.startsWith(ZuulFilterConstant.URL_SLASH)) {
start = 1;
}
int sIndex = requestUrl.indexOf(ZuulFilterConstant.URL_SLASH, start);
if (sIndex == -1) {
sIndex = requestUrl.length();
}
// 服务别名
String alias = requestUrl.substring(start, sIndex);
// 获取微服务配置
ServiceConfig serviceConfig = serviceConfigServiceImpl.getCacheByAlias(alias);
if (serviceConfig != null) {
// 设置请求上下文,和请求路径
String path = requestUrl.substring(sIndex);
serviceConfig.setPath(path);
}
return serviceConfig;
}
2.3 认证token及授权
(1) 解析token
在保存token的时候,封装了用户ID等信息,现在要把用户ID取出来了。
private AcClaimsDTO getIdByAccessToken(String accessToken) throws ServiceException {
String claimsByToken = "";
try {
claimsByToken = (String)this.tokenProvider.getClaimsByToken(accessToken);
AcClaimsDTO json = (AcClaimsDTO)JSON.parseObject(claimsByToken, AcClaimsDTO.class);
String tokenKey = String.format("login:%s:%s", json.getSource(), json.getId());
if (!StringUtils.isEmpty(claimsByToken) && !StringUtils.isBlank(this.tokenProvider.verify(accessToken, tokenKey, 1))) {
return json;
} else {
throw new ServiceException("56000078", this.getMessage("56000078"));
}
} catch (ExpiredJwtException var5) {
this.logger.info("token取对象异常,过期", var5);
throw new ServiceException("56000077", this.getMessage("56000077"));
} catch (ServiceException var6) {
throw var6;
} catch (Exception var7) {
this.logger.info("token取对象异常,无效", var7);
throw new ServiceException("56000066", this.getMessage("56000066"));
}
}
(2)根据用户ID获取查询用户信息
Result<List<UserQueryResult>> Result = userManagerService.query(userQueryParam, context);
if (Result == null || CollectionUtils.isEmpty(Result.getData())) {
return Result.fail(AuthRespCodeEnum.RESPONSE_USER_GET_FAIL.getCode(),
AuthRespCodeEnum.RESPONSE_USER_GET_FAIL.getMessage());
}
UserQueryResult user = Result.getData().get(0);
(3)获取存储的token的key值(存到redis和数据库中了)
String loginTokenKey = String.format(RedisConstant.LOGIN_TOKEN_KEY, LoginSourceEnum.WEB.getCode(), user.getId());
String value = cacheService.get(loginTokenKey);
if (value == null) {
value = cacheService.getDB(loginTokenKey);
}
JwtTokenCacheResult jwtTokenCacheResult = JSON.parseObject(value, JwtTokenCacheResult.class);
if (jwtTokenCacheResult == null) {
loginTokenKey = String.format(RedisConstant.LOGIN_TOKEN_KEY, LoginSourceEnum.APP.getCode(), user.getId());
value = cacheService.get(loginTokenKey);
if (value == null) {
value = cacheService.getDB(loginTokenKey);
}
jwtTokenCacheResult = JSON.parseObject(value, JwtTokenCacheResult.class);
if (jwtTokenCacheResult == null) {
return Result.fail(AuthRespCodeEnum.RESPONSE_50001224.getCode(), AuthRespCodeEnum.RESPONSE_50001224.getMessage());
}
}
(4)判断token是否过期
if (cacheService.getExpireTime(loginTokenKey) < EXPIRE_LEFT_TIME) {
cacheService.extendExpireTime(loginTokenKey, EXTEND_EXPIR_TIME);
}
(5)验证当前用户是否有访问当前API的权限
先根据用户userId查询用户角色,再通过角色查询用户拥有的资源,判断用户是否拥有该接口的使用权限
private List<ApiResult> getApiList(UserApiQueryParam param, CallContext context) throws ServiceException {
List<ApiResult> apiList = null;
UserRoleQueryParam userRoleQueryParam = new UserRoleQueryParam();
userRoleQueryParam.setUserId(param.getUserId());
List<RoleInfoResult> roleList = this.roleService.queryByUserId(userRoleQueryParam, context);
List<String> roleIds = Collections3.extractToList(roleList, "id");
List<ResourceTreeResult> resourceList = null;
if (!CollectionUtils.isEmpty(roleIds)) {
RoleResQueryParam roleResQueryParam = new RoleResQueryParam();
roleResQueryParam.setRoleIds(roleIds);
resourceList = this.resourceService.queryByRoleIds(roleResQueryParam, context);
List<ResourceTreeResult> resourceList = CollectionUtils.isEmpty(resourceList) ? new ArrayList() : resourceList;
List<String> resourceIds = Collections3.extractToList((Collection)resourceList, "id");
param.setUserId(param.getUserId());
param.setRoleIds(roleIds);
param.setResourceIds(resourceIds);
apiList = this.apiService.queryByRoleIdsAndResourceIds(param, context);
}
if (null == apiList) {
apiList = new ArrayList();
}
if (this.rbacApplicationEnabled) {
List<ApiResult> list = this.apiService.queryByUserIdWithApplication(param);
if (!CollectionUtils.isEmpty(list)) {
((List)apiList).addAll(list);
}
}
return Lists.newArrayList(Sets.newHashSet((Iterable)apiList));
}
@ParamsValidate
public Result<Boolean> auth(UserApiAuthParam param, CallContext context) throws ServiceException {
ApiQueryDTO apiQueryDTO = new ApiQueryDTO();
apiQueryDTO.setUri(param.getUrl());
apiQueryDTO.setExt1(ApiAuthTypeEnum.COMMON.getCode());
List<Api> commonApiList = this.apiMapper.query(apiQueryDTO);
if (null != commonApiList && !commonApiList.isEmpty()) {
return Result.success(true);
} else {
UserApiQueryParam userApiQueryParam = new UserApiQueryParam();
userApiQueryParam.setUserId(param.getUserId());
List<ApiResult> apiList = this.getApiList(userApiQueryParam, context);
Boolean authFlag = false;
Iterator var8 = apiList.iterator();
while(var8.hasNext()) {
ApiResult item = (ApiResult)var8.next();
if (this.matchUrl(item.getUri(), param.getUrl())) {
authFlag = true;
break;
}
}
return Result.success(authFlag);
}
}
2.4 请求转发
private void preContext(String serviceId, String path, RequestContext requestContext, JSONObject param) {
String serviceCode = null;
String deviceId = null;
if (null != param && null != param.get("json_content")) {
JSONObject jsonContent = JSONObject.parseObject(param.get("json_content").toString());
if (null != jsonContent && null != jsonContent.get("device_id") && StringUtils.isNotEmpty(jsonContent.get(
"device_id").toString())) {
deviceId = jsonContent.get("device_id").toString();
CwGeDevice device = DeviceService.getCacheByDeviceCode(deviceId);
if (device != null) {
serviceCode = device.getServiceCode();
}
}
}
UrlForward urlForward = urlForwardServiceImpl.getCacheByUrl(path, serviceCode);
if (urlForward != null) {
serviceId = urlForward.getServiceId();
}
/*if ("/device/node/recordUploadCompare".equals(path) || "/device/node/pictureRecordUpload".equals(path)) {
serviceId = "davinci-vip";
}*/
log.debug("网关转发服务:serviceID=" + serviceId + ";path=" + path + ";deviceId=" + deviceId + ";serviceCode=" + serviceCode);
requestContext.set(FilterConstants.SERVICE_ID_KEY, serviceId);
requestContext.set(FilterConstants.RETRYABLE_KEY, true);
requestContext.set(FilterConstants.REQUEST_URI_KEY, path);
}