diff --git a/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/converter/ClientUserAccessTokenConverter.java b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/converter/ClientUserAccessTokenConverter.java new file mode 100644 index 0000000..02cb7c3 --- /dev/null +++ b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/converter/ClientUserAccessTokenConverter.java @@ -0,0 +1,26 @@ +package com.cm.common.plugin.converter; + +import com.cm.common.config.properties.OauthClientProperties; +import com.cm.common.config.properties.OauthProperties; +import com.cm.common.plugin.oauth.token.ClientTokenManager; +import com.cm.common.plugin.utils.RestTemplateUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; + +/** + * @ClassName: UserAccessTokenConverter + * @Description: 用户jwt token + * @Author: WangGeng + * @Date: 2019/2/28 3:26 PM + * @Version: 1.0 + **/ +public class ClientUserAccessTokenConverter extends DefaultAccessTokenConverter { + + public ClientUserAccessTokenConverter(OauthProperties oauthProperties, RestTemplateUtil restTemplateUtil) { + super(); + ClientUserAuthConverter clientUserAuthConverter = new ClientUserAuthConverter(); + clientUserAuthConverter.setOauthProperties(oauthProperties); + clientUserAuthConverter.setRestTemplateUtil(restTemplateUtil); + super.setUserTokenConverter(clientUserAuthConverter); + } +} diff --git a/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/converter/ClientUserAuthConverter.java b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/converter/ClientUserAuthConverter.java new file mode 100644 index 0000000..908ed21 --- /dev/null +++ b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/converter/ClientUserAuthConverter.java @@ -0,0 +1,111 @@ +package com.cm.common.plugin.converter; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.cm.common.config.properties.OauthClientProperties; +import com.cm.common.config.properties.OauthProperties; +import com.cm.common.plugin.oauth.token.ClientTokenManager; +import com.cm.common.plugin.utils.RestTemplateUtil; +import com.cm.common.pojo.bos.*; +import com.cm.common.utils.authority.AuthorityUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; +import org.springframework.util.StringUtils; + +import java.util.*; + +/** + * @ClassName: UserAuthConverter + * @Description: 重写用户认证 + * @Author: WangGeng + * @Date: 2019/2/27 4:57 PM + * @Version: 1.0 + **/ +public class ClientUserAuthConverter implements UserAuthenticationConverter { + + private static final Logger LOG = LoggerFactory.getLogger(ClientUserAuthConverter.class); + private RestTemplateUtil restTemplateUtil; + private OauthProperties oauthProperties; + + public ClientUserAuthConverter() { + } + + @Override + public Map convertUserAuthentication(Authentication authentication) { + return new LinkedHashMap(); + } + + @Override + public Authentication extractAuthentication(Map map) { + Object principal = map.get("user_name"); + if (!Objects.isNull(principal)) { + Collection authorities; + if("admin".equals(principal.toString())) { + authorities = new LinkedHashSet<>(); + authorities.add(new RoleGrantedAuthority("ROLE_ALL")); + authorities.add(new RoleGrantedAuthority("ROLE_GROUP_ALL")); + } else { + authorities = getAuthorities(map); + } + // 包含用户信息,则直接抽取其中的用户信息 + Map userInfo = (Map) map.get("user_info"); + UserInfoBO userInfoBO = new UserInfoBO(); + userInfoBO.setUserId(userInfo.get("userId").toString()); + userInfoBO.setUserUsername(userInfo.get("username").toString()); + userInfoBO.setUserName(userInfo.get("userName").toString()); + userInfoBO.setUserPhone(userInfo.get("userPhone") == null ? "" : userInfo.get("userPhone").toString()); + principal = userInfoBO; + LOG.debug("获取用户权限"); + return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities); + } else { + return null; + } + } + + private Collection getAuthorities(Map map) { + Collection authorities = (Collection) map.get("authorities"); + if(authorities.isEmpty()) { + authorities = new LinkedHashSet(); + authorities.add(new RoleGrantedAuthority("ROLE_ALL")); + authorities.add(new RoleGrantedAuthority("ROLE_GROUP_ALL")); + return authorities; + } + Map params = new HashMap<>(1); + params.put("access_token", ClientTokenManager.getInstance().getClientToken().getAccessToken()); + String result = this.restTemplateUtil.doGetFormNormal(String.format("%s/resource/role/listrolebo/%s", oauthProperties.getOauthServer(), StringUtils.collectionToDelimitedString(authorities, "_")), params); + if (Objects.isNull(result)) { + throw new IllegalArgumentException("权限不足,无法获取角色权限信息"); + } + JSONArray resultArray = JSONArray.parseArray(result); + Set roleGrantedAuthoritySet = new LinkedHashSet<>(); + for (int i = 0; i < resultArray.size(); i++) { + JSONObject resultObj = resultArray.getJSONObject(i); + RoleBO roleBO = resultObj.toJavaObject(RoleBO.class); + RoleGrantedAuthority roleGrantedAuthority = new RoleGrantedAuthority(roleBO.getRoleId(), roleBO); + roleGrantedAuthoritySet.add(roleGrantedAuthority); + } + return roleGrantedAuthoritySet; + } + + public RestTemplateUtil getRestTemplateUtil() { + return restTemplateUtil; + } + + public void setRestTemplateUtil(RestTemplateUtil restTemplateUtil) { + this.restTemplateUtil = restTemplateUtil; + } + + public OauthProperties getOauthProperties() { + return oauthProperties; + } + + public void setOauthProperties(OauthProperties oauthProperties) { + this.oauthProperties = oauthProperties; + } +} diff --git a/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/oauth/service/rbac/IClientRbacService.java b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/oauth/service/rbac/IClientRbacService.java new file mode 100644 index 0000000..5c5b523 --- /dev/null +++ b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/oauth/service/rbac/IClientRbacService.java @@ -0,0 +1,28 @@ +package com.cm.common.plugin.oauth.service.rbac; + +import org.springframework.security.core.Authentication; + +import javax.servlet.http.HttpServletRequest; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: IClientRbacService + * @Description: 客户端RBAC权限校验 + * @Author: WangGeng + * @Date: 2019/11/11 3:27 下午 + * @Version: 1.0 + **/ +public interface IClientRbacService { + + /** + * 权限校验 + * + * @param request + * @param authentication + * @return + */ + boolean hasPermission(HttpServletRequest request, Authentication authentication); + +} diff --git a/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/oauth/service/rbac/impl/ClientRbacServiceImpl.java b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/oauth/service/rbac/impl/ClientRbacServiceImpl.java new file mode 100644 index 0000000..f630512 --- /dev/null +++ b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/oauth/service/rbac/impl/ClientRbacServiceImpl.java @@ -0,0 +1,256 @@ +package com.cm.common.plugin.oauth.service.rbac.impl; + +import com.cm.common.config.properties.AccessControl; +import com.cm.common.plugin.oauth.service.rbac.IClientRbacService; +import com.cm.common.pojo.bos.RoleGrantedAuthority; +import com.cm.common.pojo.bos.RoleMenuBO; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: ClientRbacServiceImpl + * @Description: 客户端RBAC权限校验 + * @Author: WangGeng + * @Date: 2019/11/11 3:27 下午 + * @Version: 1.0 + **/ +@Component("clientRbacService") +public class ClientRbacServiceImpl implements IClientRbacService { + + private static final Logger LOG = LoggerFactory.getLogger(ClientRbacServiceImpl.class); + /** + * 根路径 + */ + private static final String ANT_PATH_BASE = "/"; + @Autowired + private AccessControl accessControl; + + @Override + public boolean hasPermission(HttpServletRequest request, Authentication authentication) { + boolean hasPermission = false; + Object principal = authentication.getPrincipal(); + if (Objects.isNull(principal) || StringUtils.equals("anonymousUser", principal.toString())) { + return false; + } + String requestURI = request.getRequestURI(); + Collection grantedAuthorities = authentication.getAuthorities(); + AntPathMatcher antPathMatcher = new AntPathMatcher(); + String contextPath = request.getContextPath(); + for (GrantedAuthority grantedAuthority : grantedAuthorities) { + RoleGrantedAuthority roleGrantedAuthority = (RoleGrantedAuthority) grantedAuthority; + if (StringUtils.contains(roleGrantedAuthority.getAuthority(), "_ALL")) { + LOG.debug("权限校验URI:{},当前用户为最高管理员,有所有权限", requestURI); + hasPermission = true; + break; + } + // 放行权限 + if (hasPassPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) { + LOG.debug("权限校验URI:{},有新增权限", requestURI); + hasPermission = true; + break; + } + // 新增权限 + if (hasSavePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) { + LOG.debug("权限校验URI:{},有新增权限", requestURI); + hasPermission = true; + break; + } + // 删除权限 + if (hasDeletePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) { + LOG.debug("权限校验URI:{},有删除权限", requestURI); + hasPermission = true; + break; + } + // 修改权限 + if (hasUpdatePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) { + LOG.debug("权限校验URI:{},有修改权限", requestURI); + hasPermission = true; + break; + } + // 查询权限 + if (hasQueryPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) { + LOG.debug("权限校验URI:{},有查询权限", requestURI); + hasPermission = true; + break; + } + } + return hasPermission; + } + + /** + * 放行权限 + * + * @param uri + * @param roleGrantedAuthority + * @param antPathMatcher + * @return + */ + private boolean hasPassPermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) { + if ((contextPath + ANT_PATH_BASE).equals(uri)) { + return true; + } + List passPaths = accessControl.getPassPaths(); + for (String passPath : passPaths) { + if (antPathMatcher.match(contextPath + passPath, uri)) { + return true; + } + } + return false; + } + + /** + * 接口的新增权限 + * + * @param uri + * @param roleGrantedAuthority + * @param antPathMatcher + * @return + */ + private boolean hasSavePermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) { + List savePaths = accessControl.getSavePaths(); + // 匹配接口 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiSaveMenu()) { + for (String savePath : savePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getApiPrefix() + savePath, uri)) { + return true; + } + } + } + // 匹配资源 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceSaveMenu()) { + for (String savePath : savePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + savePath, uri)) { + return true; + } + } + } + // 匹配路由 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getRouteSaveMenu()) { + for (String savePath : savePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getRoutePrefix() + savePath, uri)) { + return true; + } + } + } + return false; + } + + /** + * 接口的删除权限 + * + * @param uri + * @param roleGrantedAuthority + * @param antPathMatcher + * @return + */ + private boolean hasDeletePermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) { + List deletePaths = accessControl.getDeletePaths(); + // 匹配接口 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiDeleteMenu()) { + for (String deletePath : deletePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getApiPrefix() + deletePath, uri)) { + return true; + } + } + } + // 匹配资源 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceDeleteMenu()) { + for (String deletePath : deletePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + deletePath, uri)) { + return true; + } + } + } + return false; + } + + /** + * 接口的修改权限 + * + * @param uri + * @param roleGrantedAuthority + * @param antPathMatcher + * @return + */ + private boolean hasUpdatePermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) { + List updatePaths = accessControl.getUpdatePaths(); + // 匹配接口 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiUpdateMenu()) { + for (String updatePath : updatePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getApiPrefix() + updatePath, uri)) { + return true; + } + } + } + // 匹配资源 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceUpdateMenu()) { + for (String updatePath : updatePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + updatePath, uri)) { + return true; + } + } + } + // 匹配路由 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getRouteUpdateMenu()) { + for (String updatePath : updatePaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getRoutePrefix() + updatePath, uri)) { + return true; + } + } + } + return false; + } + + /** + * 接口的查询权限 + * + * @param uri + * @param roleGrantedAuthority + * @param antPathMatcher + * @return + */ + private boolean hasQueryPermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) { + List queryPaths = accessControl.getQueryPaths(); + // 匹配接口 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiQueryMenu()) { + for (String queryPath : queryPaths) { + String queryAntPath = contextPath + roleMenuBO.getApiPrefix() + queryPath; + if (antPathMatcher.match(queryAntPath, uri)) { + return true; + } + } + } + // 匹配资源 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceQueryMenu()) { + for (String queryPath : queryPaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + queryPath, uri)) { + return true; + } + } + } + // 匹配路由 + for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getRouteQueryMenu()) { + for (String queryPath : queryPaths) { + if (antPathMatcher.match(contextPath + roleMenuBO.getRoutePrefix() + queryPath, uri)) { + return true; + } + } + } + return false; + } + +} diff --git a/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/utils/RestTemplateUtil.java b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/utils/RestTemplateUtil.java index 5c96ec6..e60b23d 100644 --- a/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/utils/RestTemplateUtil.java +++ b/cloud-common-plugin-oauth/src/main/java/com/cm/common/plugin/utils/RestTemplateUtil.java @@ -52,6 +52,7 @@ public class RestTemplateUtil { params.put("access_token", accessToken); RestTemplate restTemplate = getRestTemplate(); try { + LOG.debug("Rest Get Form:\nurl: {},\nparams: {}", url, params); return getResponse(restTemplate.getForEntity(String.format("%s?%s", url, queryParams(params)), String.class, params)); } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -73,6 +74,7 @@ public class RestTemplateUtil { httpHeaders.add("token", token); HttpEntity httpEntity = new HttpEntity<>(null, httpHeaders); try { + LOG.debug("Rest get form for app:\ntoken: {},\nurl: {},\nparams: {}", token, url, params); return getResponse(restTemplate.exchange(String.format("%s?%s", url, queryParams(params)), HttpMethod.GET, httpEntity, String.class)); } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -90,6 +92,7 @@ public class RestTemplateUtil { public String doGetFormNormal(String url, Map params) { RestTemplate restTemplate = getRestTemplate(); try { + LOG.debug("Rest get form normal:\nurl: {},\nparams: {}", url, params); return getResponse(restTemplate.getForEntity(String.format("%s?%s", url, queryParams(params)), String.class, params)); } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -167,6 +170,7 @@ public class RestTemplateUtil { HttpEntity> httpEntity = new HttpEntity<>(queryParams, httpHeaders); RestTemplate restTemplate = getRestTemplate(); try { + LOG.debug("Rest post:\nurl: {},\nparams: {},\nhttpHeaders: {},\nqueryParams: {}", url, params, httpHeaders, queryParams); return getResponse(restTemplate.postForEntity(url, httpEntity, String.class)); } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -181,6 +185,7 @@ public class RestTemplateUtil { HttpEntity httpEntity = new HttpEntity<>(JSONObject.toJSONString(params), httpHeaders); RestTemplate restTemplate = getRestTemplate(); try { + LOG.debug("Rest post for app:\ntoken: {},\nurl: {},\nparams: {}", token, url, params); return getResponse(restTemplate.postForEntity(url, httpEntity, String.class)); } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -213,9 +218,11 @@ public class RestTemplateUtil { * @return */ private String getResponse(ResponseEntity responseEntity) { - LOG.debug(">>>> 请求状态:" + responseEntity.getStatusCodeValue()); + LOG.debug(">>>> 请求结果状态: {}, ", responseEntity.getStatusCodeValue()); if (HttpStatus.OK.value() == responseEntity.getStatusCodeValue()) { - return responseEntity.getBody(); + String response = responseEntity.getBody(); + LOG.debug(">>>> 返回结果: {}", response); + return response; } else if (HttpStatus.UNAUTHORIZED.value() == responseEntity.getStatusCodeValue()) { return null; } diff --git a/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/routes/file/FileRouteController.java b/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/routes/file/FileRouteController.java index 3372ed7..7ccd726 100644 --- a/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/routes/file/FileRouteController.java +++ b/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/routes/file/FileRouteController.java @@ -37,7 +37,6 @@ public class FileRouteController extends AbstractController { @Autowired private FileProperties fileProperties; - @ApiOperation(value = "打开文件上传", notes = "打开文件上传接口") @ApiImplicitParams({ @ApiImplicitParam(name = "uploadType", value = "文件类型,1:文件,2:图片,3:视频,4:音频", paramType = "path") @@ -46,6 +45,21 @@ public class FileRouteController extends AbstractController { @GetMapping("uploadfile/{uploadType}") public ModelAndView uploadFile(@PathVariable("uploadType") String uploadType) throws ParamsException { ModelAndView mv = new ModelAndView("file/file-upload"); + return getUploadFileModelAndView(mv, uploadType); + } + + @ApiOperation(value = "打开文件上传V2", notes = "打开文件上传V2接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "uploadType", value = "文件类型,1:文件,2:图片,3:视频,4:音频", paramType = "path") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @GetMapping("uploadfile/v2/{uploadType}") + public ModelAndView uploadFileV2(@PathVariable("uploadType") String uploadType) throws ParamsException { + ModelAndView mv = new ModelAndView("file/file-upload-v2"); + return getUploadFileModelAndView(mv, uploadType); + } + + private ModelAndView getUploadFileModelAndView(ModelAndView mv, String uploadType) throws ParamsException { if (!IFileService.UPLOAD_FILE_TYPE.equals(uploadType) && !IFileService.UPLOAD_IMAGE_TYPE.equals(uploadType) && !IFileService.UPLOAD_VIDEO_TYPE.equals(uploadType) && @@ -86,12 +100,7 @@ public class FileRouteController extends AbstractController { public ModelAndView uploadImage() { Map params = requestParams(); ModelAndView mv = new ModelAndView("file/image-upload"); - if (!StringUtils.isBlank(params.get("fileId") == null ? null : params.get("fileId").toString())) { - mv.addObject("fileId", params.get("fileId")); - } else { - mv.addObject("fileId", ""); - } - return mv; + return getUploadImageModelAndView(mv, params); } /** @@ -103,6 +112,10 @@ public class FileRouteController extends AbstractController { public ModelAndView uploadImageV2() { Map params = requestParams(); ModelAndView mv = new ModelAndView("file/image-upload-v2"); + return getUploadImageModelAndView(mv, params); + } + + private ModelAndView getUploadImageModelAndView(ModelAndView mv, Map params) { if (!StringUtils.isBlank(params.get("fileId") == null ? null : params.get("fileId").toString())) { mv.addObject("fileId", params.get("fileId")); } else { diff --git a/cloud-common-plugin/src/main/resources/templates/file/file-upload-v2.html b/cloud-common-plugin/src/main/resources/templates/file/file-upload-v2.html new file mode 100644 index 0000000..49b90e2 --- /dev/null +++ b/cloud-common-plugin/src/main/resources/templates/file/file-upload-v2.html @@ -0,0 +1,65 @@ + + + + + + + + + + + +
+
+
+ +
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/cloud-common-plugin/src/main/resources/templates/tree/tree-v2.html b/cloud-common-plugin/src/main/resources/templates/tree/tree-v2.html index 7cf2dd6..c4b76a0 100644 --- a/cloud-common-plugin/src/main/resources/templates/tree/tree-v2.html +++ b/cloud-common-plugin/src/main/resources/templates/tree/tree-v2.html @@ -7,9 +7,9 @@ + -