|
@@ -0,0 +1,325 @@
|
|
|
+package com.zkqy.framework.sso_oauth2.controller;
|
|
|
+
|
|
|
+import com.zkqy.common.constant.CacheConstants;
|
|
|
+import com.zkqy.common.core.domain.AjaxResult;
|
|
|
+import com.zkqy.common.core.domain.entity.SysUser;
|
|
|
+import com.zkqy.common.core.redis.RedisCache;
|
|
|
+import com.zkqy.common.enums.sso.ErrorCodeEnum;
|
|
|
+import com.zkqy.common.enums.sso.ExpireEnum;
|
|
|
+import com.zkqy.common.enums.sso.GrantTypeEnum;
|
|
|
+import com.zkqy.common.utils.StringUtils;
|
|
|
+import com.zkqy.common.utils.sso.DateUtils;
|
|
|
+import com.zkqy.framework.security.context.AuthenticationContextHolder;
|
|
|
+import com.zkqy.system.domain.sso.AuthAccessToken;
|
|
|
+import com.zkqy.system.domain.sso.AuthClientDetails;
|
|
|
+import com.zkqy.system.domain.sso.AuthRefreshToken;
|
|
|
+import com.zkqy.system.service.ISysUserService;
|
|
|
+import com.zkqy.system.service.sso.AuthorizationService;
|
|
|
+import com.zkqy.system.service.sso.RedisService;
|
|
|
+
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.security.authentication.AuthenticationManager;
|
|
|
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
|
+import org.springframework.security.core.Authentication;
|
|
|
+import org.springframework.security.core.AuthenticationException;
|
|
|
+import org.springframework.stereotype.Controller;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 基于oauth2.0相关的授权相关操作
|
|
|
+ */
|
|
|
+@Controller
|
|
|
+@RequestMapping("/oauth2")
|
|
|
+public class OauthController {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisService redisService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AuthorizationService authorizationService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ISysUserService userService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisCache redisCache;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private AuthenticationManager authenticationManager;
|
|
|
+
|
|
|
+ @Value("${OpenAuthorization2.MES.REDIRECT_URL}")
|
|
|
+ private String REDIRECT_URL;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取Authorization Code
|
|
|
+ */
|
|
|
+ @PostMapping("/authorize")
|
|
|
+ @ResponseBody
|
|
|
+ public Map<String, Object> authorize(HttpServletRequest request) {
|
|
|
+ String tenantID = request.getParameter("tenantID");
|
|
|
+ String username = request.getParameter("username");
|
|
|
+ String password = request.getParameter("password");
|
|
|
+ String code = request.getParameter("code");
|
|
|
+ String uuid = request.getParameter("uuid");
|
|
|
+ String verifyKey =
|
|
|
+ CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
|
|
|
+ String captcha = redisCache.getCacheObject(verifyKey);
|
|
|
+ if (captcha == null || !captcha.equals(code))
|
|
|
+// if (false)
|
|
|
+ {
|
|
|
+ return AjaxResult.warn("验证码错误");
|
|
|
+ } else {
|
|
|
+ redisCache.deleteObject(verifyKey);
|
|
|
+ }
|
|
|
+ String endUsername;
|
|
|
+ // 单点登录
|
|
|
+ if (tenantID != null && !tenantID.isEmpty()) {
|
|
|
+ endUsername = tenantID + "¥¥¥" + username;
|
|
|
+ } else {
|
|
|
+ endUsername = username;
|
|
|
+ }
|
|
|
+ SysUser sysUser = userService.queryUserInfo(endUsername, password);
|
|
|
+ // 用户验证
|
|
|
+ Authentication authentication = null;
|
|
|
+ try {
|
|
|
+ UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(endUsername, password);
|
|
|
+ AuthenticationContextHolder.setContext(authenticationToken);
|
|
|
+ // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
|
|
+ authentication = authenticationManager.authenticate(authenticationToken);
|
|
|
+ } catch (AuthenticationException e) {
|
|
|
+ return AjaxResult.warn("用户名或密码错误");
|
|
|
+ // return "用户名或密码错误";
|
|
|
+ }
|
|
|
+ String clientIdStr = request.getParameter("client_id");
|
|
|
+ String scopeStr = request.getParameter("scope");
|
|
|
+ String redirectUri = request.getParameter("redirect_uri");
|
|
|
+ String status = request.getParameter("status");
|
|
|
+ String authorizationCode = authorizationService.createAuthorizationCode(clientIdStr, scopeStr, sysUser);
|
|
|
+ String params =
|
|
|
+ redirectUri + "?code=" + authorizationCode;
|
|
|
+ if (StringUtils.isNoneBlank(status)) {
|
|
|
+ params = params + "&status=" + status;
|
|
|
+ }
|
|
|
+ // request.getSession().setAttribute(Constants.SESSION_USER, sysUser);
|
|
|
+ // 存放redis用户信息数据
|
|
|
+ redisCache.setCacheObject(authorizationCode, sysUser);
|
|
|
+ return AjaxResult.success(authorizationCode, params);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取Authorization Code
|
|
|
+ */
|
|
|
+ @GetMapping("/authorize")
|
|
|
+ public String getAuthorize(HttpServletRequest request) {
|
|
|
+ String key = request.getParameter("key");
|
|
|
+ String tenantCode = request.getParameter("tenantCode");
|
|
|
+ if (key != null && !key.isEmpty()) {
|
|
|
+ SysUser sysUser = redisService.get(key);
|
|
|
+ String clientIdStr = request.getParameter("client_id");
|
|
|
+ String scopeStr = request.getParameter("scope");
|
|
|
+ String redirectUri = request.getParameter("redirect_uri");
|
|
|
+ String status = request.getParameter("status");
|
|
|
+ String authorizationCode = authorizationService.createAuthorizationCode(clientIdStr, scopeStr, sysUser);
|
|
|
+ String params =
|
|
|
+ redirectUri + "?code=" + authorizationCode;
|
|
|
+ if (StringUtils.isNoneBlank(status)) {
|
|
|
+ params = params + "&status=" + status;
|
|
|
+ }
|
|
|
+ // redisService.delete(key);
|
|
|
+ return "redirect:" + params;
|
|
|
+ } else {
|
|
|
+ return "redirect:" + REDIRECT_URL + "/login?tenantCode=" + tenantCode;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过Authorization Code获取Access Token.
|
|
|
+ */
|
|
|
+ @PostMapping(value = "/access-token", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
|
|
+ @ResponseBody
|
|
|
+ public Map<String, Object> token(HttpServletRequest request) {
|
|
|
+ Map<String, Object> result = new HashMap<>(8);
|
|
|
+ String grantType = request.getParameter("grant_type");
|
|
|
+ String code = request.getParameter("code");
|
|
|
+ String clientIdStr = request.getParameter("client_id");
|
|
|
+ String clientSecret = request.getParameter("client_secret");
|
|
|
+ String redirectUri = request.getParameter("redirect_uri");
|
|
|
+ System.out.println(grantType);
|
|
|
+ System.out.println(GrantTypeEnum.AUTHORIZATION_CODE.getType());
|
|
|
+ //校验授权方式
|
|
|
+ if (!GrantTypeEnum.AUTHORIZATION_CODE.getType().equals(grantType)) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.UNSUPPORTED_GRANT_TYPE);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ AuthClientDetails savedClientDetails = authorizationService.selectClientDetailsByClientId(
|
|
|
+ clientIdStr);
|
|
|
+ if (!(savedClientDetails != null && savedClientDetails.getClientSecret()
|
|
|
+ .equals(clientSecret))) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.INVALID_CLIENT);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ //校验回调URL
|
|
|
+ if (!savedClientDetails.getRedirectUri().equals(redirectUri)) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.REDIRECT_URI_MISMATCH);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ //从Redis获取允许访问的用户权限范围
|
|
|
+ String scope = redisService.get(code + ":scope");
|
|
|
+ //从Redis获取对应的用户信息
|
|
|
+ SysUser sysUser = redisService.get(code + ":user");
|
|
|
+
|
|
|
+ //如果能够通过Authorization Code获取到对应的用户信息,则说明该Authorization Code有效
|
|
|
+ if (StringUtils.isNoneBlank(scope) && sysUser != null) {
|
|
|
+ // 过期时间
|
|
|
+ Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());
|
|
|
+ // 生成Access Token
|
|
|
+ String accessTokenStr = authorizationService.createAccessToken(sysUser, savedClientDetails,
|
|
|
+ grantType, scope, expiresIn);
|
|
|
+ // 查询已经插入到数据库的Access Token
|
|
|
+ AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessTokenStr);
|
|
|
+ // 生成Refresh Token
|
|
|
+ String refreshTokenStr = authorizationService.createRefreshToken(sysUser, authAccessToken);
|
|
|
+ //返回数据
|
|
|
+ result.put("access_token", authAccessToken.getAccessToken());
|
|
|
+ result.put("refresh_token", refreshTokenStr);
|
|
|
+ result.put("expires_in", expiresIn);
|
|
|
+ result.put("scope", scope);
|
|
|
+ } else {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.INVALID_GRANT);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ } catch (Exception e) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过Refresh Token刷新Access Token.
|
|
|
+ */
|
|
|
+ @RequestMapping(value = "/refresh-token", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
|
|
+ @ResponseBody
|
|
|
+ public Map<String, Object> refreshToken(HttpServletRequest request) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ //获取Refresh Token
|
|
|
+ String refreshTokenStr = request.getParameter("refresh_token");
|
|
|
+ try {
|
|
|
+ AuthRefreshToken authRefreshToken = authorizationService.selectByRefreshToken(
|
|
|
+ refreshTokenStr);
|
|
|
+ if (authRefreshToken != null) {
|
|
|
+ Long savedExpiresAt = authRefreshToken.getExpiresIn();
|
|
|
+ //过期日期
|
|
|
+ LocalDateTime expiresDateTime = DateUtils.ofEpochSecond(savedExpiresAt, null);
|
|
|
+ //当前日期
|
|
|
+ LocalDateTime nowDateTime = DateUtils.now();
|
|
|
+ //如果Refresh Token已经失效,则需要重新生成
|
|
|
+ if (expiresDateTime.isBefore(nowDateTime)) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.EXPIRED_TOKEN);
|
|
|
+ } else {
|
|
|
+ //获取存储的Access Token
|
|
|
+ AuthAccessToken authAccessToken = authorizationService.selectByAccessId(
|
|
|
+ authRefreshToken.getTokenId());
|
|
|
+ //获取对应的客户端信息
|
|
|
+ AuthClientDetails savedClientDetails = authorizationService.selectClientDetailsById(
|
|
|
+ authAccessToken.getClientId());
|
|
|
+ //获取对应的用户信息
|
|
|
+ SysUser sysUser = userService.selectUserById(Long.valueOf(authAccessToken.getUserId().toString()));
|
|
|
+
|
|
|
+ //新的过期时间
|
|
|
+ Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());
|
|
|
+ //生成新的Access Token
|
|
|
+ String newAccessTokenStr = authorizationService.createAccessToken(sysUser, savedClientDetails
|
|
|
+ , authAccessToken.getGrantType(), authAccessToken.getScope(), expiresIn);
|
|
|
+ //返回数据
|
|
|
+ result.put("access_token", newAccessTokenStr);
|
|
|
+ result.put("refresh_token", refreshTokenStr);
|
|
|
+ result.put("expires_in", expiresIn);
|
|
|
+ result.put("scope", authAccessToken.getScope());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.INVALID_GRANT);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ } catch (Exception e) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验Access Token
|
|
|
+ */
|
|
|
+ @RequestMapping(value = "/verify", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
|
|
+ @ResponseBody
|
|
|
+ public Map<String, Object> verify(HttpServletRequest request) {
|
|
|
+ Map<String, Object> result = new HashMap<>(8);
|
|
|
+
|
|
|
+ //获取Access Token
|
|
|
+ String accessToken = request.getParameter("access_token");
|
|
|
+ //获取Access Token
|
|
|
+ String refreshToken = request.getParameter("refresh_token");
|
|
|
+ try {
|
|
|
+ //过期时间
|
|
|
+ Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());
|
|
|
+ //查询Access Token
|
|
|
+ AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessToken);
|
|
|
+ //查询Refresh Token
|
|
|
+ AuthRefreshToken ssoRefreshToken = authorizationService.selectByRefreshToken(refreshToken);
|
|
|
+ //组装返回信息
|
|
|
+ result.put("access_token", authAccessToken.getAccessToken());
|
|
|
+ result.put("refresh_token", ssoRefreshToken.getRefreshToken());
|
|
|
+ result.put("expires_in", expiresIn);
|
|
|
+ return result;
|
|
|
+ } catch (Exception e) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验Access Token,并返回用户信息
|
|
|
+ */
|
|
|
+ @GetMapping(value = "/user-info", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
|
|
+ @ResponseBody
|
|
|
+ public Map<String, Object> getUserInfo(HttpServletRequest request) {
|
|
|
+ Map<String, Object> result = new HashMap<>(8);
|
|
|
+ //获取Access Token
|
|
|
+ String accessToken = request.getParameter("access_token");
|
|
|
+ try {
|
|
|
+ AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessToken);
|
|
|
+ //查询用户信息
|
|
|
+ SysUser sysUser = userService.selectUserById(authAccessToken.getUserId());
|
|
|
+ //组装返回信息
|
|
|
+ result.put("access_token", authAccessToken.getAccessToken());
|
|
|
+ result.put("user_info", sysUser);
|
|
|
+ return result;
|
|
|
+ } catch (Exception e) {
|
|
|
+ this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组装错误请求的返回
|
|
|
+ */
|
|
|
+ private void generateErrorResponse(Map<String, Object> result, ErrorCodeEnum errorCodeEnum) {
|
|
|
+ result.put("error", errorCodeEnum.getError());
|
|
|
+ result.put("error_description", errorCodeEnum.getErrorDescription());
|
|
|
+ }
|
|
|
+
|
|
|
+}
|