Spring Security是一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面程式設計)功能,為應用系統提供宣告式的安全訪問控制功能,減少了為企業系統安全控制編寫大量重複程式碼的工作。
前言
spring security 3.0
已經可以使用spring el
表示式來控制授權,允許在表示式中使用複雜的布林邏輯來控制訪問的許可權。
常見的表示式
Spring Security可用表示式物件的基類是SecurityExpressionRoot。
表示式 | 描述 |
---|---|
hasRole([role] ) |
使用者擁有制定的角色時返回true (Spring security 預設會帶有ROLE_ 字首),去除參考Remove the ROLE_ |
hasAnyRole([role1,role2]) |
使用者擁有任意一個制定的角色時返回true |
hasAuthority([authority]) |
等同於hasRole ,但不會帶有ROLE_ 字首 |
hasAnyAuthority([auth1,auth2]) |
等同於hasAnyRole |
permitAll |
永遠返回true |
denyAll |
永遠返回false |
anonymous |
當前使用者是anonymous 時返回true |
rememberMe |
當前勇士是rememberMe 使用者返回true |
authentication |
當前登入使用者的authentication 物件 |
fullAuthenticated |
當前使用者既不是anonymous 也不是rememberMe 使用者時返回true |
hasIpAddress('192.168.1.0/24')) |
請求傳送的IP匹配時返回true |
部分程式碼:
......
private String defaultRolePrefix = "ROLE_"; //ROLE_字首
/** Allows "permitAll" expression */
public final boolean permitAll = true; //全部true
/** Allows "denyAll" expression */
public final boolean denyAll = false; //全部false
public final boolean permitAll() {
return true;
}
public final boolean denyAll() {
return false;
}
public final boolean isAnonymous() {
//是否是anonymous
return trustResolver.isAnonymous(authentication);
}
public final boolean isRememberMe() {
//是否是rememberme
return trustResolver.isRememberMe(authentication);
}
......
複製程式碼
URL安全表示式
onfig.antMatchers("/person/*").access("hasRole('ADMIN') or hasRole('USER')")
.anyRequest().authenticated();
複製程式碼
這裡我們定義了應用/person/*
URL的範圍,該URL只針對擁有ADMIN
或者USER
許可權的使用者有效。
在Web安全表示式中引用bean
config.antMatchers("/person/*").access("hasRole('ADMIN') or hasRole('USER')")
.antMatchers("/person/{id}").access("@rbacService.checkUserId(authentication,#id)")
.anyRequest()
.access("@rbacService.hasPermission(request,authentication)");
複製程式碼
RbacServiceImpl
@Component("rbacService")
@Slf4j
public class RbacServiceImpl implements RbacService {
/**
* uri匹配工具
*/
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
log.info("【RbacServiceImpl】 --hasPermission={}", authentication.getPrincipal());
Object principal = authentication.getPrincipal();
boolean hasPermission = false;
//有可能是匿名的anonymous
if (principal instanceof SysUser) {
//admin永遠放回true
if (StringUtils.equals("admin", ((SysUser) principal).getUsername())) {
hasPermission = true;
} else {
//讀取使用者所擁有許可權所有的URL 在這裡全部返回true
Set<String> urls = new HashSet<>();
for (String url : urls) {
if (antPathMatcher.match(url, request.getRequestURI())) {
hasPermission = true;
break;
}
}
}
}
return hasPermission;
}
public boolean checkUserId(Authentication authentication, int id) {
return true;
}
}
複製程式碼
效果如下:
Method安全表示式
針對方法級別的訪問控制比較複雜,Spring Security
提供了四種註解,分別是@PreAuthorize
, @PreFilter
, @PostAuthorize
和 @PostFilter
使用method註解
- 開啟方法級別註解的配置
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MerryyouSecurityConfig extends WebSecurityConfigurerAdapter {
複製程式碼
- 配置相應的bean
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@ConditionalOnMissingBean(PasswordEncoder.class)
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
複製程式碼
- 在方法上面使用註解
/**
* 查詢所有人員
*/
@PreAuthorize("hasRole('ADMIN')")
@ApiOperation(value = "獲得person列表", notes = "")
@GetMapping(value = "/persons")
public List<Person> getPersons() {
return personService.findAll();
}
複製程式碼
PreAuthorize
@PreAuthorize 註解適合進入方法前的許可權驗證
@PreAuthorize("hasRole('ADMIN')")
List<Person> findAll();
複製程式碼
PostAuthorize
@PostAuthorize 在方法執行後再進行許可權驗證,適合驗證帶有返回值的許可權。Spring EL
提供 返回物件能夠在表示式語言中獲取返回的物件returnObject
。
@PostAuthorize("returnObject.name == authentication.name")
Person findOne(Integer id);
複製程式碼
PreAuthorize 針對引數進行過濾
//當有多個物件是使用filterTarget進行標註
@PreFilter(filterTarget="ids", value="filterObject%2==0")
public void delete(List<Integer> ids, List<String> usernames) {
...
}
複製程式碼
PostFilter 針對返回結果進行過濾
@PreAuthorize("hasRole('ADMIN')")
@PostFilter("filterObject.name == authentication.name")
List<Person> findAll();
複製程式碼
效果如下:
程式碼下載
從我的 github 中下載,github.com/longfeizhen…