bigywater.tech
Spring · 109

授权决策:AuthorizationManager 与 ROLE_ 前缀

hasRole 和 hasAuthority 差在哪?ROLE_ 前缀什么时候要写?规则顺序写反了会怎样?角色继承怎么配?怎么写一个自己的授权决策?这篇在 Spring Boot 4.0.8、Spring Security 7 上用三个用户逐一验证。

1 个用户放行
hasRole("ADMIN") 在三个用户里只放行 root;权限叫 ADMIN 的 eve 被拒绝
200 而不是 403
规则顺序写反:alice 访问 /a/admin/x 通过,没有任何报错
403 → 200
声明 RoleHierarchy "ROLE_ADMIN > ROLE_USER" 后,root 访问 /user

角色与权限

实测 · 本机执行输出
  规则:/user hasRole(USER);/admin hasRole(ADMIN);/byauth hasAuthority(ROLE_ADMIN);/noprefix hasAuthority(ADMIN);/either hasAnyRole(USER, ADMIN)
    GET /user       alice→200;root→403;eve→403
    GET /admin      alice→403;root→200;eve→403
    GET /byauth     alice→403;root→200;eve→403
    GET /noprefix   alice→403;root→403;eve→200
    GET /either     alice→200;root→200;eve→403
  hasRole("ROLE_ADMIN"):应用启动失败:Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'chain' defined in demo.Main: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'chain' threw exception with message: ROLE_ADMIN should not start with ROLE_ since ROLE_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead. | Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'chain' threw exception with message: ROLE_ADMIN should not start with ROLE_ since ROLE_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.

hasRole("X") 就是 hasAuthority("ROLE_X");写成 hasRole("ROLE_X") 启动失败。

规则顺序

实测 · 本机执行输出
  正确顺序:先 /a/admin/** hasRole(ADMIN),再 /a/** hasRole(USER):
    GET /a/x        alice→200;root→403;eve→403
    GET /a/admin/x  alice→403;root→200;eve→403
  写反:先 /a/** hasRole(USER),再 /a/admin/** hasRole(ADMIN):
    GET /a/x        alice→200;root→403;eve→403
    GET /a/admin/x  alice→200;root→403;eve→403
  anyRequest().authenticated() 写在前面,后面又写 requestMatchers("/admin"):应用启动失败:Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'chain' defined in demo.Main: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'chain' threw exception with message: Can't configure requestMatchers after anyRequest | Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'chain' threw exception with message: Can't configure requestMatchers after anyRequest

第一个匹配的生效;具体的规则要写在宽泛的前面;anyRequest 之后不能再写。

角色继承与自定义决策

实测 · 本机执行输出
  没有 RoleHierarchy:/user 需要 hasRole(USER):
    GET /user       alice→200;root→403;eve→403
  声明 RoleHierarchy bean "ROLE_ADMIN > ROLE_USER":
    GET /user       alice→200;root→200;eve→403
    GET /admin      alice→403;root→200;eve→403
  自定义 AuthorizationManager(只允许本机、带 X-Internal: yes、并且用户是 alice):
    GET /custom 用户 alice,头 {'X-Internal': 'yes'} → 200
    GET /custom 用户 alice,头 {} → 403
    GET /custom 用户 root,头 {'X-Internal': 'yes'} → 403
  服务端记录的 custom manager 调用:
    custom manager:用户=alice,远端=127.0.0.1,X-Internal=yes → true
    custom manager:用户=alice,远端=127.0.0.1,X-Internal=null → false
    custom manager:用户=root,远端=127.0.0.1,X-Internal=yes → false

RoleHierarchy bean 让高级角色拥有低级角色;AuthorizationManager 可以按请求与用户自定义。

分步演示

授权
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动(Spring Security 7)。InMemoryUserDetailsManager 里三个用户(alice、root、eve);HTTP Basic 认证;表中数字是 HTTP 状态码;每个场景启动一次应用,切换 -Dx109.cfg;自定义 AuthorizationManager 记录的是进程输出里的内容
    • 没有验证:requestMatchers 的 MVC 与 PathPattern 匹配差异(如尾部斜杠、后缀);AuthorizationManagers.anyOf / allOf 组合;方法级安全里使用 RoleHierarchy(见第 108 篇);自定义 GrantedAuthorityDefaults 修改 ROLE_ 前缀;ACL 与对象级授权;在 Spring Security 7 里 AuthorizationResult 的其他实现
    京ICP备2026067502号-1京公网安备11010502063487号