Просмотр исходного кода

引入 HandlerInterceptor 带来的连锁反应, 由于使用了自定义的 WebMvcConfigurationSupport, 使得默认的 Web 自动配置失效. 首先是调用 RequestContextHolder.getRequestAttributes 返回 null, 其次是 Hibernate 的 LazyInitializationException 异常以及在 controller 里面不能自动将 id 转化为 entity. 本次提交就是为了解决这些问题

reghao 2 лет назад
Родитель
Сommit
117f3b3f1d

+ 4 - 6
oss-web/src/main/java/cn/reghao/oss/web/account/controller/RoleController.java

@@ -11,7 +11,6 @@ import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -48,7 +47,8 @@ public class RoleController {
 
     @ApiOperation("获取角色可访问的资源")
     @GetMapping(value = "/menus/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
-    public String getRoleMenus(@PathVariable("id") Role role) {
+    public String getRoleMenus(@PathVariable("id") int roleId) {
+        Role role = roleService.getRole(roleId);
         List<Menu> allMenus = menuQuery.findAll();
         allMenus.forEach(menu -> {
             // TODO 序列化时会递归 roles 中的 Role 导致 StackOverflow, 因此在序列化时应该把 Role 的 menus 设置为 null
@@ -67,10 +67,8 @@ public class RoleController {
     @ApiOperation("设置角色可访问的资源")
     @PostMapping(value = "/menus", produces = MediaType.APPLICATION_JSON_VALUE)
     public String setRoleMenus(@RequestParam(value = "id") Integer roleId,
-                               @RequestParam(value = "menuId", required = false) Set<Menu> menus) {
-        if (menus == null) {
-            menus = Collections.emptySet();
-        }
+                               @RequestParam(value = "menuId") List<Integer> menuIds) {
+        Set<Menu> menus = menuQuery.getMenus(menuIds);
         roleService.setRoleMenus(roleId, menus);
         return WebResult.success();
     }

+ 1 - 2
oss-web/src/main/java/cn/reghao/oss/web/account/controller/page/UserPageController.java

@@ -123,9 +123,8 @@ public class UserPageController {
 
     @ApiOperation(value = "用户角色分配页面")
     @GetMapping("/role/{id}")
-    public String assignRolePage(@PathVariable("id") User user, Model model) {
+    public String assignRolePage(@PathVariable("id") Integer userId, Model model) {
         Set<Role> roles = new HashSet<>(roleQuery.findAll());
-        int userId = user.getId();
         Set<Role> authRoles = userQuery.getUserRoles(userId);
 
         model.addAttribute("id", userId);

+ 1 - 0
oss-web/src/main/java/cn/reghao/oss/web/account/db/query/MenuQuery.java

@@ -12,4 +12,5 @@ import java.util.*;
 public interface MenuQuery extends BaseQuery<Menu> {
     List<Menu> getSortedMenusByStatus(Boolean isEnabled);
     Map<Integer, String> getSortedChildGroupByPid(int pid);
+    Set<Menu> getMenus(List<Integer> menuIds);
 }

+ 5 - 0
oss-web/src/main/java/cn/reghao/oss/web/account/db/query/MenuQueryImpl.java

@@ -51,4 +51,9 @@ public class MenuQueryImpl implements MenuQuery {
         menus.forEach(menu -> map.put(menu.getPos(), menu.getName()));
         return map;
     }
+
+    @Override
+    public Set<Menu> getMenus(List<Integer> menuIds) {
+        return new HashSet<>(menuRepository.findAllById(menuIds));
+    }
 }

+ 2 - 1
oss-web/src/main/java/cn/reghao/oss/web/account/model/po/Menu.java

@@ -2,6 +2,7 @@ package cn.reghao.oss.web.account.model.po;
 
 import cn.reghao.oss.web.util.db.BaseEntity;
 import lombok.*;
+import org.hibernate.annotations.Proxy;
 
 import javax.persistence.*;
 import javax.validation.constraints.NotBlank;
@@ -39,7 +40,7 @@ public class Menu extends BaseEntity {
     // Menu 拥有的所有子 Menu(按排序顺序, 不持久化)
     @Transient
     private Map<Integer, Menu> children;
-    @ManyToMany(mappedBy = "menus")
+    @ManyToMany(mappedBy = "menus", fetch = FetchType.EAGER)
     @NotNull(message = "角色不能为 NULL")
     private Set<Role> roles;
     @Transient

+ 3 - 1
oss-web/src/main/java/cn/reghao/oss/web/account/model/po/Role.java

@@ -5,6 +5,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 import lombok.ToString;
+import org.hibernate.annotations.Proxy;
 import org.hibernate.validator.constraints.Length;
 
 import javax.persistence.*;
@@ -19,6 +20,7 @@ import java.util.Set;
 @Data
 @EqualsAndHashCode(callSuper = false, exclude = {"description", "menus"})
 @ToString(exclude = {"menus"})
+@Proxy(lazy = false)
 @Table(name = "sys_role")
 @Entity
 public class Role extends BaseEntity {
@@ -30,7 +32,7 @@ public class Role extends BaseEntity {
     @Length(max = 100, message = "对角色描述的长度不超过 100 个中文字符")
     private String description;
     // Role 端维护 Role 和 Menu 之间的关系
-    @ManyToMany
+    @ManyToMany(fetch = FetchType.EAGER)
     @JoinTable(name = "sys_role_menu",
             joinColumns = @JoinColumn(name = "role_id"),
             inverseJoinColumns = @JoinColumn(name = "menu_id"))

+ 2 - 0
oss-web/src/main/java/cn/reghao/oss/web/account/model/po/User.java

@@ -3,6 +3,7 @@ package cn.reghao.oss.web.account.model.po;
 import cn.reghao.oss.web.util.db.BaseEntity;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.*;
+import org.hibernate.annotations.Proxy;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 
@@ -22,6 +23,7 @@ import java.util.stream.Collectors;
 @NoArgsConstructor
 @Getter
 @Setter
+@Proxy(lazy = false)
 @Table(name = "sys_user")
 @Entity
 public class User extends BaseEntity implements UserDetails {

+ 1 - 0
oss-web/src/main/java/cn/reghao/oss/web/account/service/RoleService.java

@@ -14,4 +14,5 @@ public interface RoleService {
     void addOrModify(Role role);
     void delete(Integer roleId);
     void setRoleMenus(Integer roleId, Set<Menu> menus);
+    Role getRole(int roleId);
 }

+ 5 - 0
oss-web/src/main/java/cn/reghao/oss/web/account/service/impl/RoleServiceImpl.java

@@ -74,4 +74,9 @@ public class RoleServiceImpl implements RoleService {
         role.setMenus(menus);
         roleRepository.save(role);
     }
+
+    @Override
+    public Role getRole(int roleId) {
+        return roleRepository.getOne(roleId);
+    }
 }

+ 71 - 0
oss-web/src/main/java/cn/reghao/oss/web/config/web/ConsoleAuthInterceptor.java

@@ -0,0 +1,71 @@
+package cn.reghao.oss.web.config.web;
+
+import cn.reghao.jutil.web.ServletUtil;
+import cn.reghao.oss.web.app.service.UserKeyService;
+import cn.reghao.oss.web.util.AuthKeyContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * oss-console 认证拦截
+ *
+ * @author reghao
+ * @date 2024-02-27 11:54:07
+ */
+@Slf4j
+@Component
+public class ConsoleAuthInterceptor implements HandlerInterceptor {
+    private final Set<String> ignoreUrls = new HashSet<>();
+    private final UserKeyService userKeyService;
+
+    public ConsoleAuthInterceptor(UserKeyService userKeyService) {
+        this.userKeyService = userKeyService;
+        this.ignoreUrls.addAll(List.of("/api/oss/key/auth",
+                "/api/oss/store/node/register",
+                "/api/oss/channel",
+                "/api/oss/channels"));
+    }
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String uri = request.getRequestURI();
+        String method = request.getMethod();
+        if (uri.startsWith("/api/oss/") && !method.equalsIgnoreCase("options")) {
+            if (ignoreUrls.contains(uri)) {
+                return true;
+            }
+
+            String token = ServletUtil.getBearerToken();
+            if (token == null || token.isBlank()) {
+                log.error("request {} start with /api/oss/, but not auth", uri);
+                response.setStatus(403);
+                return false;
+            } else {
+                int userId = userKeyService.getUserIdFromToken(token);
+                log.info("{} access {}", userId, uri);
+                AuthKeyContext authKeyContext = new AuthKeyContext(userId);
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response,
+                           Object handler, @Nullable ModelAndView modelAndView) throws Exception {
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+                                Object handler, @Nullable Exception ex) throws Exception {
+    }
+}

+ 50 - 0
oss-web/src/main/java/cn/reghao/oss/web/config/web/WebConfig.java

@@ -0,0 +1,50 @@
+package cn.reghao.oss.web.config.web;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.context.request.RequestContextListener;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+
+/**
+ * @author reghao
+ * @date 2021-12-30 12:34:26
+ */
+@Configuration
+public class WebConfig extends WebMvcConfigurationSupport {
+    private final ConsoleAuthInterceptor consoleAuthInterceptor;
+
+    public WebConfig(ConsoleAuthInterceptor consoleAuthInterceptor) {
+        this.consoleAuthInterceptor = consoleAuthInterceptor;
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(consoleAuthInterceptor);
+    }
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("/css/**")
+                .addResourceLocations("classpath:/static/css/");
+        registry.addResourceHandler("/imgs/**")
+                .addResourceLocations("classpath:/static/imgs/");
+        registry.addResourceHandler("/js/**")
+                .addResourceLocations("classpath:/static/js/");
+        registry.addResourceHandler("/lib/**")
+                .addResourceLocations("classpath:/static/lib/");
+    }
+
+    /**
+     * TODO 解决 RequestContextHolder.getRequestAttributes() 空指针问题
+     *
+     * @param
+     * @return
+     * @date 2024-02-27 14:34:56
+     */
+    @Bean
+	public RequestContextListener requestContextListener(){
+		return new RequestContextListener();
+	}
+}

+ 22 - 0
oss-web/src/main/java/cn/reghao/oss/web/util/AuthKeyContext.java

@@ -0,0 +1,22 @@
+package cn.reghao.oss.web.util;
+
+/**
+ * @author reghao
+ * @date 2023-06-02 10:48:59
+ */
+public class AuthKeyContext implements AutoCloseable {
+    static final ThreadLocal<Integer> CURRENT = new ThreadLocal<>();
+
+    public AuthKeyContext(Integer user) {
+        CURRENT.set(user);
+    }
+
+    public static int getUser() {
+        return CURRENT.get();
+    }
+
+    @Override
+    public void close() {
+        CURRENT.remove();
+    }
+}