提交所有代码
This commit is contained in:
@@ -16,6 +16,7 @@ import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
|
||||
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
|
||||
import com.ruoyi.framework.security.filter.LeaderReadOnlyFilter;
|
||||
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
|
||||
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
|
||||
|
||||
@@ -80,6 +81,8 @@ public class SecurityConfig
|
||||
})
|
||||
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
|
||||
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
// leader 只读拦截: 在 JWT 鉴权之后 (SecurityContext 已就绪), 任何业务写操作前拦下
|
||||
.addFilterAfter(new LeaderReadOnlyFilter(), JwtAuthenticationTokenFilter.class)
|
||||
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
|
||||
.addFilterBefore(corsFilter, LogoutFilter.class)
|
||||
.build();
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package com.ruoyi.framework.security.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 「项目负责人」(leader) 角色只读拦截.
|
||||
*
|
||||
* <p>leader 是纯查看角色: 除了查看自己的资料/改密码 (账号信息页), 不允许任何业务写操作.
|
||||
* 后端兜底 (前端隐藏按钮之外, 防 devtools 直接调接口):
|
||||
* <ul>
|
||||
* <li>GET 请求一律放行.</li>
|
||||
* <li>非 GET (POST/PUT/DELETE) 仅放行本人资料维护: {@code /system/user/profile} 与
|
||||
* {@code /system/user/profile/updatePwd} (SysProfileController 的 GET/PUT 与改密码).</li>
|
||||
* <li>其余非 GET 一律 403.</li>
|
||||
* </ul>
|
||||
* 与 JwtAuthenticationTokenFilter 之后注册 (SecurityConfig), 依赖其已写入 SecurityContext.
|
||||
*/
|
||||
public class LeaderReadOnlyFilter extends OncePerRequestFilter
|
||||
{
|
||||
/** 放行的本人资料维护路径 (账号信息页) */
|
||||
private static final String[] SELF_SERVICE_ALLOWED = {
|
||||
"/system/user/profile",
|
||||
"/system/user/profile/updatePwd",
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
if (HttpMethod.GET.matches(request.getMethod())
|
||||
|| !isLeader()
|
||||
|| isSelfService(request.getRequestURI()))
|
||||
{
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
// leader 非 GET 且非本人资料维护 → 只读拒绝
|
||||
response.setStatus(HttpStatus.FORBIDDEN);
|
||||
ServletUtils.renderString(response, JSON.toJSONString(
|
||||
AjaxResult.error(HttpStatus.FORBIDDEN, "项目负责人无操作权限")));
|
||||
}
|
||||
|
||||
/** 当前登录人是否为 leader (role_type 单一可信源), 未登录/异常视为非 leader (放行交给后续鉴权) */
|
||||
private boolean isLeader()
|
||||
{
|
||||
try
|
||||
{
|
||||
return "leader".equals(SecurityUtils.getLoginUser().getUser().getRoleType());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 是否命中本人资料维护白名单 (账号信息页: 查看/修改资料 + 改密码) */
|
||||
private boolean isSelfService(String uri)
|
||||
{
|
||||
if (StringUtils.isEmpty(uri))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (String path : SELF_SERVICE_ALLOWED)
|
||||
{
|
||||
if (uri.equals(path) || uri.startsWith(path + "/"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user