@PreAuthorize and @PostAuthorize

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

开启验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 : zhulongkun20@gmail.com
* @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注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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 : zhulongkun20@gmail.com
* @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表达式,

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

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

@PostAuthorize

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

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

1
2
3
4
5
6
7
8
9
10
11
12
@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可以使用。

1
2
3
4
5
@GetMapping("/helloUser")
@Secured({"ROLE_admin", "ROLE_normal"})
public String helloUser() {
return "hello, user";
}

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