2021-05-29 15:32  阅读(174)
文章分类:Spring Security 源码分析 文章标签:Spring SecuritySpring Security 源码
©  原文作者: 郑龙飞 原文地址:https://juejin.cn/user/4212984285237870

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(Springsecurity默认会带有ROLE_前缀),去除参考RemovetheROLE_
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;
        }
    }
    复制代码

效果如下:

202105291531584351.png https://user-gold-cdn.xitu.io/2018/1/30/16147b3f89291c64?w=1190&h=601&f=gif&s=1412669

Method安全表达式

针对方法级别的访问控制比较复杂,Spring Security提供了四种注解,分别是@PreAuthorize , @PreFilter , @PostAuthorize@PostFilter

使用method注解

  1. 开启方法级别注解的配置
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class MerryyouSecurityConfig extends WebSecurityConfigurerAdapter {
    复制代码
  1. 配置相应的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 注解适合进入方法前的权限验证

    @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();
    复制代码

效果如下:

https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-el02.gif https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-el02.gif

代码下载

从我的 github 中下载,github.com/longfeizhen…

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring Security源码分析十三:Spring Security 基于表达式的权限控制
上一篇
Spring Security源码分析十二:Spring Security OAuth2基于JWT实现单点登录
下一篇
Spring Security源码分析十四:Spring Social社交登录绑定与解绑