From 1c4741e3ebd8f6c81ce9536e501e9a06d8194d59 Mon Sep 17 00:00:00 2001 From: miaoyinjun <312656362@qq.com> Date: Tue, 5 Jul 2022 13:07:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96SecurityUtils=E7=9A=84?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E5=87=8F=E5=B0=91=E4=B8=8Eredis?= =?UTF-8?q?=E4=B9=8B=E9=97=B4=E7=9A=84=E4=BA=A4=E4=BA=92=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zhengjie/config/ElPermissionConfig.java | 7 +- .../zhengjie/constant/SecurityConstant.java | 32 +++++ .../main/java/me/zhengjie/utils/HttpUtil.java | 53 ++++++++ .../java/me/zhengjie/utils/RequestHolder.java | 42 ++++++ .../java/me/zhengjie/utils/SecurityUtils.java | 120 ++++++++++++------ .../wrapper/HeaderMapRequestWrapper.java | 84 ++++++++++++ .../security/security/TokenFilter.java | 16 ++- 7 files changed, 312 insertions(+), 42 deletions(-) create mode 100644 eladmin-common/src/main/java/me/zhengjie/constant/SecurityConstant.java create mode 100644 eladmin-common/src/main/java/me/zhengjie/utils/HttpUtil.java create mode 100644 eladmin-common/src/main/java/me/zhengjie/wrapper/HeaderMapRequestWrapper.java diff --git a/eladmin-common/src/main/java/me/zhengjie/config/ElPermissionConfig.java b/eladmin-common/src/main/java/me/zhengjie/config/ElPermissionConfig.java index 9ae1fd5d..fcf5a22d 100644 --- a/eladmin-common/src/main/java/me/zhengjie/config/ElPermissionConfig.java +++ b/eladmin-common/src/main/java/me/zhengjie/config/ElPermissionConfig.java @@ -16,11 +16,10 @@ package me.zhengjie.config; import me.zhengjie.utils.SecurityUtils; -import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Service; + import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; /** * @author Zheng Jie @@ -30,7 +29,7 @@ public class ElPermissionConfig { public Boolean check(String ...permissions){ // 获取当前用户的所有权限 - List elPermissions = SecurityUtils.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); + Set elPermissions = SecurityUtils.listPermission(); // 判断当前用户的所有权限是否包含接口上定义的权限 return elPermissions.contains("admin") || Arrays.stream(permissions).anyMatch(elPermissions::contains); } diff --git a/eladmin-common/src/main/java/me/zhengjie/constant/SecurityConstant.java b/eladmin-common/src/main/java/me/zhengjie/constant/SecurityConstant.java new file mode 100644 index 00000000..5d08f49e --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/constant/SecurityConstant.java @@ -0,0 +1,32 @@ +package me.zhengjie.constant; + +/** + *

+ * 安全定义 + *

+ * + * @author miaoyj + * @version 1.0.0-SNAPSHOT + * @since 2020-08-12 + */ +public interface SecurityConstant { + + /** + * 用户id + */ + String JWT_KEY_USER_ID = "userid"; + + /** + * 用户名称 + */ + String JWT_KEY_USERNAME = "username"; + + /** + * 用户权限 + */ + String JWT_KEY_PERMISSION = "permissions"; + /** + * 用户数据范围-部门ids + */ + String JWT_KEY_DATA_SCOPE_DEPT_IDS = "data_scope_dept_ids"; +} \ No newline at end of file diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/HttpUtil.java b/eladmin-common/src/main/java/me/zhengjie/utils/HttpUtil.java new file mode 100644 index 00000000..11e4d726 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/HttpUtil.java @@ -0,0 +1,53 @@ +package me.zhengjie.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import me.zhengjie.constant.SecurityConstant; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * http + *

+ * + * @author miaoyj + * @since 2022-07-05 + */ +public class HttpUtil { + + /** + *

+ * 设置header的用户信息 + *

+ * + * @param userDetails / + * @return / + */ + public static Map getUserHeaders(UserDetails userDetails) { + Map httpHeaders = new HashMap<>(); + if (userDetails != null) { + //用户基本信息 + String userId = new JSONObject(new JSONObject(userDetails).get("user")).get("id", String.class); + String username = userDetails.getUsername(); + List elPermissions = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); + //部门id + JSONArray array = JSONUtil.parseArray(new JSONObject(userDetails).get("dataScopes")); + List dataScopeDeptIds = JSONUtil.toList(array,Long.class); + List dataScopeDeptIdsList = new ArrayList<>(); + if (CollUtil.isNotEmpty(dataScopeDeptIds)) { + dataScopeDeptIdsList = dataScopeDeptIds.stream().map(o -> String.valueOf(o)).collect(Collectors.toList()); + } + httpHeaders.put(SecurityConstant.JWT_KEY_USER_ID, userId); + httpHeaders.put(SecurityConstant.JWT_KEY_USERNAME, username); + httpHeaders.put(SecurityConstant.JWT_KEY_PERMISSION, elPermissions); + httpHeaders.put(SecurityConstant.JWT_KEY_DATA_SCOPE_DEPT_IDS, dataScopeDeptIdsList); + } + return httpHeaders; + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java b/eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java index 71a4b9e5..93d7df1d 100644 --- a/eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java +++ b/eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java @@ -15,10 +15,14 @@ */ package me.zhengjie.utils; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; import java.util.Objects; +import java.util.Set; /** * 获取 HttpServletRequest @@ -30,4 +34,42 @@ public class RequestHolder { public static HttpServletRequest getHttpServletRequest() { return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); } + + + /** + *

+ * 获取头部信息 + *

+ * + * @param headerName 头部名称 + * @return / + */ + public static String getHeader(String headerName) { + return getHttpServletRequest().getHeader(headerName); + } + + /** + *

+ * 获取头部信息 + *

+ * + * @param headerName 头部名称 + * @return / + */ + public static Long getHeaderLong(String headerName) { + return Convert.toLong(getHeader(headerName)); + } + + /** + *

+ * 获取头部集合 + *

+ * + * @param headerName 头部名称 + * @return / + */ + public static Set getHeaders(String headerName) { + Enumeration values = getHttpServletRequest().getHeaders(headerName); + return CollUtil.newHashSet(true, values); + } } diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java index 08f4c042..bf9e6117 100644 --- a/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java +++ b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java @@ -15,10 +15,10 @@ */ package me.zhengjie.utils; -import cn.hutool.json.JSONArray; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; +import me.zhengjie.constant.SecurityConstant; import me.zhengjie.exception.BadRequestException; import me.zhengjie.utils.enums.DataScopeEnum; import org.springframework.http.HttpStatus; @@ -26,10 +26,15 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; + +import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * 获取当前登录的用户 + * * @author Zheng Jie * @date 2019-01-17 */ @@ -38,11 +43,14 @@ public class SecurityUtils { /** * 获取当前登录的用户 + * * @return UserDetails */ public static UserDetails getCurrentUser() { UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class); - return userDetailsService.loadUserByUsername(getCurrentUsername()); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + return userDetailsService.loadUserByUsername(userDetails.getUsername()); } /** @@ -51,45 +59,85 @@ public class SecurityUtils { * @return 系统用户名称 */ public static String getCurrentUsername() { - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null) { - throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期"); + return getHeaderByAuthException(SecurityConstant.JWT_KEY_USERNAME); + } + + /** + * 获取系统用户ID + * + * @return 系统用户ID + */ + public static Long getCurrentUserId() { + return getLongHeaderByAuthException(SecurityConstant.JWT_KEY_USER_ID); + } + + /** + * 获取当前用户的数据权限 + * + * @return / + */ + public static List getCurrentUserDataScope() { + Set deptStrIds = RequestHolder.getHeaders(SecurityConstant.JWT_KEY_DATA_SCOPE_DEPT_IDS); + List deptIds = new ArrayList<>(); + if (CollUtil.isNotEmpty(deptStrIds)) { + deptIds = deptStrIds.stream().map(o -> Long.parseLong(o)).collect(Collectors.toList()); } - if (authentication.getPrincipal() instanceof UserDetails) { - UserDetails userDetails = (UserDetails) authentication.getPrincipal(); - return userDetails.getUsername(); + return deptIds; + } + + /** + * 获取数据权限级别 + * + * @return 级别 + */ + public static String getDataScopeType() { + List dataScopes = getCurrentUserDataScope(); + if (dataScopes.size() != 0) { + return ""; + } + return DataScopeEnum.ALL.getValue(); + } + + /** + *

+ * 获取权限列表 + *

+ * + * @return / + */ + public static Set listPermission() { + return RequestHolder.getHeaders(SecurityConstant.JWT_KEY_PERMISSION); + } + + /** + *

+ * 获取header,验证用户信息抛出异常 + *

+ * + * @author miaoyj + * @since 2022-06-23 + */ + private static String getHeaderByAuthException(String headerName) { + String value = RequestHolder.getHeader(headerName); + if (StrUtil.isNotBlank(value)) { + return value; } throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息"); } /** - * 获取系统用户ID - * @return 系统用户ID + *

+ * 获取header,验证用户信息抛出异常 + *

+ * + * @author miaoyj + * @since 2022-06-23 */ - public static Long getCurrentUserId() { - UserDetails userDetails = getCurrentUser(); - return new JSONObject(new JSONObject(userDetails).get("user")).get("id", Long.class); - } - - /** - * 获取当前用户的数据权限 - * @return / - */ - public static List getCurrentUserDataScope(){ - UserDetails userDetails = getCurrentUser(); - JSONArray array = JSONUtil.parseArray(new JSONObject(userDetails).get("dataScopes")); - return JSONUtil.toList(array,Long.class); - } - - /** - * 获取数据权限级别 - * @return 级别 - */ - public static String getDataScopeType() { - List dataScopes = getCurrentUserDataScope(); - if(dataScopes.size() != 0){ - return ""; + private static Long getLongHeaderByAuthException(String headerName) { + Long value = RequestHolder.getHeaderLong(headerName); + if (value != null && value > 0) { + return value; } - return DataScopeEnum.ALL.getValue(); + throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息"); } } diff --git a/eladmin-common/src/main/java/me/zhengjie/wrapper/HeaderMapRequestWrapper.java b/eladmin-common/src/main/java/me/zhengjie/wrapper/HeaderMapRequestWrapper.java new file mode 100644 index 00000000..2595b6b5 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/wrapper/HeaderMapRequestWrapper.java @@ -0,0 +1,84 @@ +package me.zhengjie.wrapper; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.util.*; + + +/** + *

+ * request修改header包装 + *

+ * + * @author miaoyj + * @since 2022-06-21 + */ +public class HeaderMapRequestWrapper extends HttpServletRequestWrapper { + private Map headerMap = new HashMap<>(); + + /** + * construct a wrapper for this request + * + * @param request + */ + public HeaderMapRequestWrapper(HttpServletRequest request) { + super(request); + } + + /** + * add a header with given name and value + * + * @param name + * @param value + */ + public void addHeader(String name, String value) { + headerMap.put(name, value); + } + + /** + *

+ * 批量添加 + *

+ * + * @param headers / + */ + public void addHeaders(Map headers) { + headerMap.putAll(headers); + } + + + @Override + public String getHeader(String name) { + String headerValue = super.getHeader(name); + if (headerMap.containsKey(name)) { + headerValue = String.valueOf(headerMap.get(name)); + } + return headerValue; + } + + /** + * get the Header names + */ + @Override + public Enumeration getHeaderNames() { + List names = Collections.list(super.getHeaderNames()); + for (Object name : headerMap.keySet()) { + names.add(String.valueOf(name)); + } + return Collections.enumeration(names); + } + + @Override + public Enumeration getHeaders(String name) { + List values = Collections.list(super.getHeaders(name)); + if (headerMap.containsKey(name)) { + Object value = headerMap.get(name); + if (String.class.isInstance(value)) { + values.add(String.valueOf(value)); + } else if (value != null) { + values = (List) value; + } + } + return Collections.enumeration(values); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java index d235cb81..ed81ec16 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java @@ -18,21 +18,27 @@ package me.zhengjie.modules.security.security; import cn.hutool.core.util.StrUtil; import io.jsonwebtoken.ExpiredJwtException; import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.UserCacheManager; import me.zhengjie.modules.security.service.dto.OnlineUserDto; -import me.zhengjie.modules.security.service.OnlineUserService; +import me.zhengjie.utils.HttpUtil; +import me.zhengjie.utils.SecurityUtils; +import me.zhengjie.wrapper.HeaderMapRequestWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.StringUtils; import org.springframework.web.filter.GenericFilterBean; + import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; +import java.util.Map; import java.util.Objects; /** @@ -64,6 +70,8 @@ public class TokenFilter extends GenericFilterBean { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + HeaderMapRequestWrapper reqWrapper = new HeaderMapRequestWrapper((HttpServletRequest) servletRequest); + String token = resolveToken(httpServletRequest); // 对于 Token 为空的不需要去查 Redis if (StrUtil.isNotBlank(token)) { @@ -84,9 +92,13 @@ public class TokenFilter extends GenericFilterBean { SecurityContextHolder.getContext().setAuthentication(authentication); // Token 续期 tokenProvider.checkRenewal(token); + //设置用户信息到头部 + UserDetails userDetails = SecurityUtils.getCurrentUser(); + Map userHeaders = HttpUtil.getUserHeaders(userDetails); + reqWrapper.addHeaders(userHeaders); } } - filterChain.doFilter(servletRequest, servletResponse); + filterChain.doFilter(reqWrapper, servletResponse); } /**