|
|
@@ -0,0 +1,336 @@
|
|
|
+package com.dk.oauth.controller.oauth;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.alibaba.fastjson.serializer.SerializerFeature;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.dk.common.infrastructure.constant.Constant;
|
|
|
+import com.dk.common.infrastructure.enums.ErrorCodeEnum;
|
|
|
+import com.dk.common.model.vo.core.UserVO;
|
|
|
+import com.dk.common.response.ResponseCodeEnum;
|
|
|
+import com.dk.common.response.ResponseResultUtil;
|
|
|
+import com.dk.common.response.ResponseResultVO;
|
|
|
+import com.dk.common.service.UserService;
|
|
|
+import com.dk.common.infrastructure.constant.OauthConstants;
|
|
|
+import com.dk.oauth.entity.AuthAccessToken;
|
|
|
+import com.dk.oauth.entity.AuthClient;
|
|
|
+import com.dk.oauth.entity.AuthCode;
|
|
|
+import com.dk.oauth.entity.AuthUserLoginLog;
|
|
|
+import com.dk.oauth.mapper.AuthUserLoginLogMapper;
|
|
|
+import com.dk.oauth.service.IAuthAccessTokenService;
|
|
|
+import com.dk.oauth.service.IAuthClientService;
|
|
|
+import com.dk.oauth.service.IAuthCodeService;
|
|
|
+import com.dk.oauth.shiro.jwt.JWTGenerator;
|
|
|
+import com.dk.oauth.shiro.jwt.JWTToken;
|
|
|
+import com.dk.oauth.util.AESSecurityUtil;
|
|
|
+import com.dk.oauth.util.DateUtil;
|
|
|
+import com.dk.oauth.util.IpUtil;
|
|
|
+import com.dk.oauth.util.UUID;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
|
|
|
+import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
|
|
|
+import org.apache.oltu.oauth2.as.response.OAuthASResponse;
|
|
|
+import org.apache.oltu.oauth2.common.OAuth;
|
|
|
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
|
|
|
+import org.apache.oltu.oauth2.common.message.OAuthResponse;
|
|
|
+import org.apache.oltu.oauth2.common.message.types.GrantType;
|
|
|
+import org.apache.shiro.SecurityUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
+import org.springframework.web.bind.annotation.PathVariable;
|
|
|
+import org.springframework.web.bind.annotation.PostMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestBody;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 令牌生成
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@RestController
|
|
|
+public class AccessTokenController {
|
|
|
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private IAuthCodeService authCodeService;
|
|
|
+ @Resource
|
|
|
+ private IAuthClientService authClientService;
|
|
|
+ @Resource
|
|
|
+ private IAuthAccessTokenService authAccessTokenService;
|
|
|
+ @Resource
|
|
|
+ private UserService userService;
|
|
|
+ @Resource
|
|
|
+ private AuthUserLoginLogMapper authUserLoginLogMapper;
|
|
|
+
|
|
|
+ @Value("${aes-key}")
|
|
|
+ private String AESKey;
|
|
|
+ @Value("${client-app-id}")
|
|
|
+ private String clientAppId;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @desc : 获取token
|
|
|
+ * @author : 周兴
|
|
|
+ * @date : 2023/1/5 13:35
|
|
|
+ */
|
|
|
+ @PostMapping(value = "/oauth/token")
|
|
|
+ public ResponseResultVO token(HttpServletRequest request) throws Exception {
|
|
|
+ AuthUserLoginLog authUserLoginLog = new AuthUserLoginLog();
|
|
|
+ AuthAccessToken authAccessToken = new AuthAccessToken();
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
+ headers.set("Content-Type", "application/json; charset=utf-8");
|
|
|
+ try {
|
|
|
+ // region 校验client参数
|
|
|
+
|
|
|
+ // 转为OAuth请求
|
|
|
+ OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
|
|
|
+
|
|
|
+ // 校验客户端ID是否正确
|
|
|
+ QueryWrapper queryWrapper = new QueryWrapper();
|
|
|
+ queryWrapper.eq("client_id", oauthRequest.getClientId());
|
|
|
+ AuthClient client = authClientService.getOne(queryWrapper);
|
|
|
+ if (null == client) {
|
|
|
+ log.error("获取accessToken时,客户端ID错误 client=" + oauthRequest.getClientId());
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_CLIENT_DESCRIPTION);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验客户端安全KEY是否正确
|
|
|
+ if (!oauthRequest.getClientSecret().equalsIgnoreCase(client.getClientSecret())) {
|
|
|
+ log.error("ClientSecret不合法 client_secret=" + oauthRequest.getClientSecret());
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_CLIENT_DESCRIPTION);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验authCode
|
|
|
+ String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
|
|
|
+ AuthCode code = authCodeService.getOne(new QueryWrapper<AuthCode>().eq("code", authCode));
|
|
|
+ if (client.getState() != null && client.getState().equals(AuthCode.STATE_1)) {
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_CLIENT_STOP);
|
|
|
+ }
|
|
|
+ authUserLoginLog.setClient(authCode);
|
|
|
+ //endregion
|
|
|
+
|
|
|
+ // region 根据不同grant_type处理
|
|
|
+ /**
|
|
|
+ * 只校验 AUTHORIZATION_CODE、PASSWORD 、REFRESH_TOKEN 和 CLIENT_CREDENTIALS 类型
|
|
|
+ * 具体查看 {@link GrantType}
|
|
|
+ * */
|
|
|
+ UserVO userVo = null;
|
|
|
+ //定义api用户
|
|
|
+ UserVO apiUser = null;
|
|
|
+ ResponseResultVO validRes = ResponseResultUtil.error(ResponseCodeEnum.OPERATE_FAIL);
|
|
|
+
|
|
|
+ // region AUTHORIZATION_CODE
|
|
|
+ if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
|
|
|
+ if (!code.getCode().equals(authCode)) {
|
|
|
+ authUserLoginLog.setLoginType(AuthUserLoginLog.LOGIN_TYPE0);
|
|
|
+ authUserLoginLog.setCommand(AuthUserLoginLog.LOGIN_COMMAND3);
|
|
|
+ loinLog(authUserLoginLog, request);
|
|
|
+ log.error("获取accessToken时,授权码错误");
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_CODE_DESCRIPTION);
|
|
|
+ }
|
|
|
+ authAccessToken.setUserId(code.getUserId());
|
|
|
+ }
|
|
|
+ // endregion
|
|
|
+
|
|
|
+ // region PASSWORD
|
|
|
+ else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equalsIgnoreCase(GrantType.PASSWORD.toString())) {
|
|
|
+ UserVO user = new UserVO();
|
|
|
+ user.setUserCode(request.getParameter("username")).setUserPwd(request.getParameter("password"))
|
|
|
+ .setAppCode(request.getParameter("appcode")).setFtyCode(request.getParameter("ftycode"));
|
|
|
+// ResponseResultVO<UserVO> userRes = userService.selectCpCodeByCodeOrPhone(request.getParameter("username"));
|
|
|
+// if (userRes.getCode() == ResponseCodeEnum.SUCCESS.getCode()) {
|
|
|
+// user = userRes.getData();
|
|
|
+// } else {
|
|
|
+// return userRes;
|
|
|
+// }
|
|
|
+ //调用user服务进行业务校验
|
|
|
+ validRes = userService.loginValid(user);
|
|
|
+ if (validRes.getCode() != ResponseCodeEnum.SUCCESS.getCode()) {
|
|
|
+ authUserLoginLog.setLoginType(AuthUserLoginLog.LOGIN_TYPE3);
|
|
|
+ authUserLoginLog.setCommand(AuthUserLoginLog.LOGIN_COMMAND3);
|
|
|
+ loinLog(authUserLoginLog, request);
|
|
|
+ return validRes;
|
|
|
+ } else {
|
|
|
+ userVo = JSON.parseObject(JSON.toJSONString(validRes.getData())).getObject("user", UserVO.class);
|
|
|
+ authAccessToken.setUserId(String.valueOf(userVo.getUserId()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // endregion
|
|
|
+
|
|
|
+ // region REFRESH_TOKEN
|
|
|
+ else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.REFRESH_TOKEN.toString())) {
|
|
|
+ // 不允许刷新TOKEN模式
|
|
|
+ authUserLoginLog.setCommand(AuthUserLoginLog.LOGIN_COMMAND3);
|
|
|
+ loinLog(authUserLoginLog, request);
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_GRAND_TYPE_NOTFOUND);
|
|
|
+ }
|
|
|
+ // endregion
|
|
|
+
|
|
|
+ // region CLIENT_CREDENTIALS
|
|
|
+ else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.CLIENT_CREDENTIALS.toString())) {
|
|
|
+ // 判断客户端身份
|
|
|
+ boolean isClientCredential = false;
|
|
|
+ String[] grantTypes = client.getGrantTypes().split(",");
|
|
|
+ for (String grantType : grantTypes) {
|
|
|
+ if (grantType.equals(GrantType.CLIENT_CREDENTIALS.toString())) {
|
|
|
+ isClientCredential = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果该客户端不允许使用客户端模式登陆
|
|
|
+ if (!isClientCredential) {
|
|
|
+ authUserLoginLog.setCommand(AuthUserLoginLog.LOGIN_COMMAND3);
|
|
|
+ loinLog(authUserLoginLog, request);
|
|
|
+ // 不允许客户端模式登陆
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_CLIENT_DESCRIPTION);
|
|
|
+ }
|
|
|
+ //根据客户端主键id查询用户
|
|
|
+ apiUser = userService.selectByClientId(client.getId());
|
|
|
+ //如果不存在,或已经停用
|
|
|
+ if (apiUser == null || !apiUser.getFlgValid()) {
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_CLIENT_STOP);
|
|
|
+ }
|
|
|
+ authAccessToken.setUserId(String.valueOf(apiUser.getUserId()));
|
|
|
+ }
|
|
|
+ // endregion
|
|
|
+ else {
|
|
|
+ authUserLoginLog.setCommand(AuthUserLoginLog.LOGIN_COMMAND3);
|
|
|
+ loinLog(authUserLoginLog, request);
|
|
|
+ return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), OauthConstants.INVALID_GRAND_TYPE_NOTFOUND);
|
|
|
+ }
|
|
|
+ // endregion
|
|
|
+
|
|
|
+ // region 开始生成Access Token
|
|
|
+ String username = "";
|
|
|
+ String userId = "";
|
|
|
+ if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.CLIENT_CREDENTIALS.toString())) {
|
|
|
+ // 客户端凭证
|
|
|
+ username = client.getClientId();
|
|
|
+ userId = String.valueOf(apiUser.getUserId());
|
|
|
+ } else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
|
|
|
+ // auth2 code模式
|
|
|
+ username = code.getUserId();
|
|
|
+ userId = code.getUserId();
|
|
|
+ } else {
|
|
|
+ // 密码模式
|
|
|
+ username = request.getParameter("username");
|
|
|
+ userId = String.valueOf(userVo.getUserId());
|
|
|
+ }
|
|
|
+ String lang = request.getParameter("lang");
|
|
|
+ // 应用编码
|
|
|
+ String appCode = request.getParameter("appcode");
|
|
|
+ String ftyCode = userVo.getFtyCode();
|
|
|
+ JWTGenerator jwtGenerator = new JWTGenerator();
|
|
|
+ jwtGenerator.setSalt(username);
|
|
|
+ jwtGenerator.setUsername(username);
|
|
|
+ jwtGenerator.setUserId(userId);
|
|
|
+ jwtGenerator.setFtyId(userVo.getFtyId().toString());
|
|
|
+ jwtGenerator.setFtyCode(ftyCode);
|
|
|
+ jwtGenerator.setClientId(client.getClientId());
|
|
|
+ jwtGenerator.setGrantType(oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE));
|
|
|
+ jwtGenerator.setAppCode(appCode);
|
|
|
+ jwtGenerator.setLang(lang);
|
|
|
+ OAuthIssuerImpl oAuthIssuer = new OAuthIssuerImpl(jwtGenerator);
|
|
|
+ String accessToken = oAuthIssuer.accessToken();
|
|
|
+ log.info("服务器生成的accessToken=" + accessToken);
|
|
|
+
|
|
|
+ // 保存token
|
|
|
+ authAccessToken.setId(UUID.uuid32());
|
|
|
+ authAccessToken.setClientId(client.getClientId());
|
|
|
+ authAccessToken.setTokenId(accessToken);
|
|
|
+ authAccessToken.setTokenType(oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE));
|
|
|
+ authAccessToken.setCreateDate(new Date());
|
|
|
+ authAccessToken.setAuthenticationId(authCode);
|
|
|
+ authAccessToken.setTokenExpiredSeconds(OauthConstants.EXPIRES_IN);
|
|
|
+ authAccessToken.setFtyId(userVo.getFtyId().toString());
|
|
|
+ authAccessToken.setFtyCode(userVo.getFtyCode());
|
|
|
+ log.info("---->>>SecurityUtils.getSubject().isAuthenticated() = " + SecurityUtils.getSubject().isAuthenticated());
|
|
|
+ JWTToken jwtToken = JWTToken.build(accessToken, username, userVo.getFtyId().toString(), ftyCode, username, OauthConstants.EXPIRES_IN, oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE), client.getClientId(),lang);
|
|
|
+ SecurityUtils.getSubject().login(jwtToken);
|
|
|
+ authAccessTokenService.save(authAccessToken);
|
|
|
+ if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
|
|
|
+ // 设置code失效,每个code只能使用一次
|
|
|
+ code.setState(1);
|
|
|
+ authCodeService.saveOrUpdate(code);
|
|
|
+ log.info("服务器移除auth_code=" + authCode);
|
|
|
+ }
|
|
|
+ // endregion
|
|
|
+
|
|
|
+ // region 加密accessToken
|
|
|
+ try {
|
|
|
+ accessToken = AESSecurityUtil.encrypt(AESKey, accessToken);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("sorry,accessToken({}) encode faild!!", accessToken);
|
|
|
+ }
|
|
|
+ authUserLoginLog.setCommand(AuthUserLoginLog.LOGIN_COMMAND1);
|
|
|
+ loinLog(authUserLoginLog, request);
|
|
|
+ // endregion
|
|
|
+
|
|
|
+ if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equalsIgnoreCase(GrantType.PASSWORD.toString())) {
|
|
|
+ //调用 loginAfterCheckPassword
|
|
|
+ ResponseResultVO<JSONObject> loginRes = userService.loginAfterCheckPassword(JSON.parseObject(JSON.toJSONString(validRes.getData())).fluentPut("accessToken", accessToken));
|
|
|
+
|
|
|
+ return ResponseResultUtil.success(JSON.parseObject(JSON.toJSONString(loginRes.getData(), SerializerFeature.WRITE_MAP_NULL_FEATURES))
|
|
|
+ .fluentPut("nowDate", DateUtil.dateToString(authAccessToken.getCreateDate()))
|
|
|
+ .fluentPut("expires_in", String.valueOf(OauthConstants.EXPIRES_IN)));
|
|
|
+ } else {
|
|
|
+ // region 生成OAuth响应
|
|
|
+ OAuthResponse response = OAuthASResponse
|
|
|
+ .tokenResponse(HttpServletResponse.SC_OK)
|
|
|
+ .setAccessToken(accessToken)
|
|
|
+ .setExpiresIn(String.valueOf(OauthConstants.EXPIRES_IN))
|
|
|
+ .setParam("nowDate", DateUtil.dateToString(authAccessToken.getCreateDate()))
|
|
|
+ .setParam("appCode",appCode)
|
|
|
+// .setParam("username", username)
|
|
|
+// .setParam("companyId", companyId)
|
|
|
+ .buildJSONMessage();
|
|
|
+ log.info("---->>>SecurityUtils.getSubject().isAuthenticated() =" + SecurityUtils.getSubject().isAuthenticated());
|
|
|
+ return ResponseResultUtil.success(response.getResponseStatus(), "", JSONObject.parse(response.getBody()));
|
|
|
+ // endregion
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (OAuthProblemException e) {
|
|
|
+ log.error("获取accessToken发生异常=", e);
|
|
|
+ authUserLoginLog.setCommand(AuthUserLoginLog.LOGIN_COMMAND3);
|
|
|
+ loinLog(authUserLoginLog, request);
|
|
|
+ return ResponseResultUtil.error(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存登录日志
|
|
|
+ *
|
|
|
+ * @param authUserLoginLog
|
|
|
+ * @param request
|
|
|
+ */
|
|
|
+ private void loinLog(AuthUserLoginLog authUserLoginLog, HttpServletRequest request) {
|
|
|
+ Date date = new Date();
|
|
|
+ authUserLoginLog.setCreateDate(date);
|
|
|
+ authUserLoginLog.setLastUpdateDate(date);
|
|
|
+ authUserLoginLog.setId(UUID.uuid32());
|
|
|
+ // 获取ip地址
|
|
|
+ String ipAddr = IpUtil.getIpAddr(request);
|
|
|
+ authUserLoginLog.setLastIp(ipAddr);
|
|
|
+ // 获取浏览器信息
|
|
|
+ String browser = IpUtil.getBrowser(request.getHeader("user-agent"));
|
|
|
+ authUserLoginLog.setVersion("1.0");
|
|
|
+ authUserLoginLog.setClient(browser);
|
|
|
+ authUserLoginLogMapper.insert(authUserLoginLog);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @desc : 查询用户最新token
|
|
|
+ * @author : 洪旭东
|
|
|
+ * @date : 2022-08-02 18:44
|
|
|
+ */
|
|
|
+ @PostMapping(value = "current_token/{userId}")
|
|
|
+ public String getCurrentToken(@PathVariable Long userId) {
|
|
|
+ return authAccessTokenService.getCurrentToken(userId);
|
|
|
+ }
|
|
|
+}
|