文档站点
Skip to content

授权 ​

Laravel 用 Gate::authorize()、$this->authorize()、@can 做权限控制;Spring Security 用 @PreAuthorize 方法级注解 + @EnableMethodSecurity。思想一致:在方法上声明「谁能调」,框架拦截校验。

三个角色/权限源 ​

先明确「当前用户有哪些角色」从哪来。在 UserDetailsService 里赋值:

java
return org.springframework.security.core.userdetails.User
        .withUsername(user.getUsername())
        .password(user.getPassword())
        .roles(user.getRole())          // "ADMIN" / "USER"
        .build();

roles("ADMIN") 实际赋予的权限名是 ROLE_ADMIN(框架自动加前缀)。

启用方法级安全 ​

java
@Configuration
@EnableMethodSecurity        // 开启 @PreAuthorize 等注解(默认已启用,加着保险)
public class MethodSecurityConfig { }

常用授权注解 ​

注解作用类比 Laravel
@PreAuthorize("hasRole('ADMIN')")调用前校验角色Gate::authorize('admin')
@PreAuthorize("hasAuthority('post.delete')")校验权限点can('delete', $post)
@PreAuthorize("#id == authentication.principal.id")校验资源归属策略类
@PostAuthorize方法返回后校验少用
@Secured只支持角色老注解,少用
java
@Service
public class PostService {

    @PreAuthorize("hasRole('ADMIN')")
    public void deleteAnyPost(Long id) { ... }

    @PreAuthorize("hasRole('ADMIN') or #dto.authorId == authentication.principal.id")
    public void updatePost(PostDto dto) { ... }
}

三种角色写法

SpEL匹配
hasRole('ADMIN')用户权限含 ROLE_ADMIN
hasAnyRole('ADMIN','EDITOR')任意一个
hasAuthority('post.delete')用户权限含 post.delete(角色也可以当 authority 用)

Laravel 里 roles 和 permissions 的关系同理:角色是大集合,权限点是细粒度动作。

控制器 + 注解联动 ​

java
@RestController
@RequestMapping("/api/posts")
public class PostController {

    @DeleteMapping("/{id}")
    @PreAuthorize("hasRole('ADMIN')")
    public void delete(@PathVariable Long id) {
        service.delete(id);
    }
}

访问无权限 → 抛 AccessDeniedException → Spring Security 返回 403。

403 vs 401 要分清

  • 401:没登录(未认证)→ Spring Security 过滤器链直接拦
  • 403:登录了但没权限(未授权)→ @PreAuthorize 抛 AccessDeniedException Laravel 里同理:auth 中间件 401/redirect 登录,Gate 拒绝 403。

基于对象的授权:资源归属校验 ​

Laravel 用授权策略(Policy)做「只能改自己的帖子」;Spring 在 @PreAuthorize 里写 SpEL:

java
@PreAuthorize("#post.authorId == authentication.principal.id")
public Post update(Long id, PostDto dto) {
    ...
}
  • #post 引用方法参数(参数名需编译期保留,或 @Param("post") 指定)
  • authentication.principal 是当前登录的 UserDetails,.id 取 ID

TIP

Laravel 里你写 Policy::update($user, $post) 返回 bool;Spring 在 SpEL 里做同样判断。复杂规则可以抽一个 Bean 方法:@PreAuthorize("@postPolicy.canUpdate(authentication, #post)"),规则集中在 PostPolicy 类里,更易测试。

常见权限场景对照 ​

需求LaravelSpring Security
管理员专属Gate::authorize('admin')@PreAuthorize("hasRole('ADMIN')")
多重角色Gate::any([...])hasAnyRole('A','B')
只能改自己的$this->authorize('update', $post)#post.authorId == authentication.principal.id
匿名可访问中间件跳过permitAll()
登录即可->middleware('auth').authenticated()
逻辑或Gate::any()hasRole('A') or hasRole('B')
控制器/服务层都可用都行方法级注解,任意 Bean 方法都可标注

授权失败返回自定义 JSON ​

默认 403 是框架样式。想返回统一结构,加异常处理器:

java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(AccessDeniedException.class)
    public ApiResponse<Void> handleDenied(AccessDeniedException ex) {
        return ApiResponse.of(403, "没有权限执行此操作", null);
    }
}

TIP

注意 AccessDeniedException 可能被 Spring Security 的过滤器链提前拦截(在进入 Controller 前)。若要完全统一,需要自定义 AccessDeniedHandler,见 Spring Security 深入。

小结 ​

记住四句话
授权用 @PreAuthorize + SpEL 表达式,方法级拦截
角色 hasRole('ADMIN') 匹配 ROLE_ADMIN
资源归属用 #参数 == authentication.principal.xxx
403 是「登录了但没权限」,和 401「没登录」是两回事

面向 PHP / Laravel 开发者的 Spring Boot 中文文档