@PreAuthorize and @PostAuthorize

@PreAuthorize 和 @PostAuthorize是SpringSecurity中常用的两个注解,简单了解一下他们的用法吧!

开启验证

package com.bill.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author :  [email protected]
 * @create :  10-20-2020 15:27:16
 * @description :  SpringSecurity配置类
 * @since :  v1.0
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 基于内存的方式构建两个用户,user/123456,admin/123456
        auth.inMemoryAuthentication()
                .passwordEncoder(passwordEncoder())
                .withUser("admin")
                .password(passwordEncoder().encode("123456"))
                .roles("admin");
        auth.inMemoryAuthentication()
                .passwordEncoder(passwordEncoder())
                .withUser("user")
                .password(passwordEncoder().encode("123456"))
                .roles("normal");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

@PreAuthorize注解

package com.bill.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author :  [email protected]
 * @create :  10-20-2020 15:22:13
 * @description :  HomeController
 * @since :  v1.0
 */
@Controller
@RequestMapping("/hello")
public class HelloController {

    @GetMapping("/")
    @ResponseBody
    public String sayHello() {
        return "Hello, spring security.";
    }

    @GetMapping("/helloNormal")
    @ResponseBody
    @PreAuthorize("hasAnyRole('normal')")
    public String helloNormal() {
        return "Hello, normal.";
    }

    @GetMapping("/helloAdmin")
    @ResponseBody
    @PreAuthorize("hasAnyRole('admin')")
    public String helloAdmin() {
        return "Hello, admin";
    }
}

当@EnableGlobalMethodSecurity(prePostEnabled = true)开启的时候,@PreAuthorize注解生效,支持SpringEL表达式,

// 只要有其中一个权限即可访问
@PreAuthorize("hasAnyRole("admin", "normal")")

// 同时满足条件才可以访问
@PreAuthorize("hasRole('admin') AND hasRole('normal')")

@PostAuthorize

@PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限,Spring EL 提供返回对象能够在表达式语言中获取返回的对象returnObject。

当@EnableGlobalMethodSecurity(prePostEnabled = true)的时候,@PostAuthorize可以使用:

@GetMapping("/helloUser")
@PostAuthorize("returnObject != null &&  returnObject.username == authentication.name")
public User helloUser() {
    Object pricipal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    User user;
    if ("anonymousUser".equals(pricipal)) {
        user = null;
    } else {
        user = (User) pricipal;
    }
    return user;
}

内置表达式:

表达式 备注
hasRole([role]) 如果有当前角色, 则返回 true(会自动加上 ROLE_ 前缀)
hasAnyRole([role1, role2]) 如果有任一角色即可通过校验, 返回true,(会自动加上 ROLE_ 前缀)
hasAuthority([authority]) 如果有指定权限, 则返回 true
hasAnyAuthority([authority1, authority2]) 如果有任一指定权限, 则返回true
principal 获取当前用户的 principal 主体对象
authentication 获取当前用户的 authentication 对象,
permitAll 总是返回 true, 表示全部允许
denyAll 总是返回 false, 代表全部拒绝
isAnonymous() 如果是匿名访问, 返回true
isRememberMe() 如果是remember-me 自动认证, 则返回 true
isAuthenticated() 如果不是匿名访问, 则返回true
isFullAuthenticated() 如果不是匿名访问或remember-me认证登陆, 则返回true
hasPermission(Object target, Object permission)
hasPermission(Object target, String targetType, Object permission)

@Secured

当@EnableGlobalMethodSecurity(securedEnabled = true)的时候,@Secured可以使用。

@GetMapping("/helloUser")
@Secured({"ROLE_admin", "ROLE_normal"})
public String helloUser() {
    return "hello, user";
}

拥有normal或者admin角色的用户都可以访问helloUser()方法。另外需要注意的是这里 匹配的字符串需要添加前缀“ROLE_“,如果我们要求,只有同时拥有admin & normal的用户才能访问helloUser()方法,这时候@Secured就无能为力了。