Spring Security源碼分析十三:Spring Security 基于表達(dá)式的權(quán)限控制

Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉(zhuǎn)Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,減少了為企業(yè)系統(tǒng)安全控制編寫大量重復(fù)代碼的工作。

前言

spring security 3.0已經(jīng)可以使用spring el表達(dá)式來控制授權(quán),允許在表達(dá)式中使用復(fù)雜的布爾邏輯來控制訪問的權(quán)限。

常見的表達(dá)式

Spring Security可用表達(dá)式對象的基類是SecurityExpressionRoot。

表達(dá)式 描述
hasRole([role]) 用戶擁有制定的角色時(shí)返回true (Spring security 默認(rèn)會(huì)帶有ROLE_前綴),去除參考Remove the ROLE_
hasAnyRole([role1,role2]) 用戶擁有任意一個(gè)制定的角色時(shí)返回true
hasAuthority([authority]) 等同于hasRole,但不會(huì)帶有ROLE_前綴
hasAnyAuthority([auth1,auth2]) 等同于hasAnyRole
permitAll 永遠(yuǎn)返回true
denyAll 永遠(yuǎn)返回false
anonymous 當(dāng)前用戶是anonymous時(shí)返回true
rememberMe 當(dāng)前勇士是rememberMe用戶返回true
authentication 當(dāng)前登錄用戶的authentication對象
fullAuthenticated 當(dāng)前用戶既不是anonymous也不是rememberMe用戶時(shí)返回true
hasIpAddress('192.168.1.0/24')) 請求發(fā)送的IP匹配時(shí)返回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安全表達(dá)式

onfig.antMatchers("/person/*").access("hasRole('ADMIN') or hasRole('USER')")
                .anyRequest().authenticated();

這里我們定義了應(yīng)用/person/*URL的范圍,該URL只針對擁有ADMIN或者USER權(quán)限的用戶有效。

在Web安全表達(dá)式中引用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永遠(yuǎn)放回true
            if (StringUtils.equals("admin", ((SysUser) principal).getUsername())) {
                hasPermission = true;
            } else {
                //讀取用戶所擁有權(quán)限所有的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;
    }
}

效果如下:

[圖片上傳失敗...(image-eb4674-1517326338238)]

Method安全表達(dá)式

針對方法級別的訪問控制比較復(fù)雜,Spring Security提供了四種注解,分別是@PreAuthorize , @PreFilter , @PostAuthorize@PostFilter

使用method注解

  1. 開啟方法級別注解的配置
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MerryyouSecurityConfig extends WebSecurityConfigurerAdapter {
  1. 配置相應(yīng)的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();
    }
  1. 在方法上面使用注解
 /**
     * 查詢所有人員
     */
    @PreAuthorize("hasRole('ADMIN')")
    @ApiOperation(value = "獲得person列表", notes = "")
    @GetMapping(value = "/persons")
    public List<Person> getPersons() {
        return personService.findAll();
    }
PreAuthorize

@PreAuthorize 注解適合進(jìn)入方法前的權(quán)限驗(yàn)證

@PreAuthorize("hasRole('ADMIN')")
    List<Person> findAll();
PostAuthorize

@PostAuthorize 在方法執(zhí)行后再進(jìn)行權(quán)限驗(yàn)證,適合驗(yàn)證帶有返回值的權(quán)限。Spring EL 提供 返回對象能夠在表達(dá)式語言中獲取返回的對象returnObject。

@PostAuthorize("returnObject.name == authentication.name")
    Person findOne(Integer id);
PreAuthorize 針對參數(shù)進(jìn)行過濾
//當(dāng)有多個(gè)對象是使用filterTarget進(jìn)行標(biāo)注
@PreFilter(filterTarget="ids", value="filterObject%2==0")
   public void delete(List<Integer> ids, List<String> usernames) {
      ...

   }
PostFilter 針對返回結(jié)果進(jìn)行過濾
 @PreAuthorize("hasRole('ADMIN')")
    @PostFilter("filterObject.name == authentication.name")
    List<Person> findAll();

效果如下:

[圖片上傳失敗...(image-98cc57-1517326338239)]

代碼下載

從我的 github 中下載,https://github.com/longfeizheng/logback

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容