当前位置: 首页 > 科技观察

SpringSecurity权限控制系列(六)

时间:2023-03-12 03:10:25 科技观察

环境:Springboot2.4.12+SpringSecurity5.4.9本文主要内容:业务接口权限认证上一篇:《??Spring Security权限控制系列(五)??》demo案例有如下接口:@RestController@RequestMapping("/business")publicclassBusinessController{@GetMapping("/{id}")publicObjectget(@PathVariable("id")Integerid){return"receive-"+id;}}安全配置:@ConfigurationpublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.csrf().disable();http.authorizeRequests().antMatchers("/resources/**","/cache/**","/process/login").permitAll();http.authorizeRequests().antMatchers("/demos/**").hasRole("USERS");http.authorizeRequests().antMatchers("/api/**").hasRole("ADMIN");//上面的配置是基于上一篇文章的,我们这里不用管,直接看下面的接口配置界面//这里我们会请求所有以/businesshttp开头的请求。authorizeRequests().antMatchers("/business/**").authenticated();}}通过上面的配置,启动服务访问http://localhost:8080/business/100接口时,会需要登录,只要登录成功,就可以访问该接口。这里我不想通过以下方式设置权限://hasRole("xxx")orhasAuthority("xxxx")http.authorizeRequests().antMatchers("/business/**").hasRole("xxx")这种写法由于权限固定,限制了所有以/business开头的请求。/business可能有很多子接口。我们可能需要为每个子接口定义不同的权限才可以访问。这时候如果通过上面的模式配置就太麻烦了。SpringSecurity也提供了基于访问注解的控制定义细化接口权限,然后使用基于注解的方法控制Controller接口权限。注意:并不是说基于注解的权限控制只能应用在Controller上,而是我们一般会在Controller中添加;实际上,可以使用任何服务方法。这些注解也可以直接添加到接口方法中。启用方法认证@Configuration@EnableGlobalMethodSecurity(jsr250Enabled=true,prePostEnabled=true,securedEnabled=true)publicclassSecurityConfigextendsWebSecurityConfigurerAdapter{}属性说明:jsr250Enabled:启用对JSR-250注解的支持。@RolesAllowed。prePostEnabled:启用基于表达式的语法支持(jsr250Enabled和securedEnabled都是简单的基于角色的约束)。@预授权。securedEnabled:启用对@Secured注释的支持。示例:@GetMapping("/{id}")@RolesAllowed("ROLE_USERS")//①@Secured("ROLE_USERS1")//②@PreAuthorize("hasRole('USERS')")//③publicObjectget(@PathVariable("id")Integerid){return"receive-"+id;}接收一个String[]数组,可以定义多个角色。接收一个String[]数组,可以定义多个角色。可以使用SpEL表达式。本文只演示基于@PreAuthorize注解的权限控制,其他两个很简单,不再演示。PreAuthorizeAnnotation使用此注释指定方法访问控制表达式,该表达式将被评估以确定是否允许方法调用。默认支持以下表达式:示例1:您必须具有USERS角色才能访问该界面。@PreAuthorize("hasRole('USERS')")publicObjectget(@PathVariable("id")Integerid){return"receive-"+id;}示例2:访问该接口,只需要有任何的角色可以。@PreAuthorize("hasAnyRole('USERS','ADMIN')")publicObjectget(@PathVariable("id")Integerid){return"receive-"+id;}例三:访问这个接口必须有一个巴士:新闻:见许可。@PreAuthorize("hasAuthority('bus:news:see')")publicObjectget(@PathVariable("id")Integerid){return"receive-"+id;}例子四:这个接口只需要有任何以下权限都可以。@PreAuthorize("hasAnyAuthority('bus:news:see','bus:news:write')")publicObjectget(@PathVariable("id")Integerid){return"receive-"+id;}注意:这里的hasRole和hasAuthority有什么区别?授权认证中使用的表达式根对象的基类是SecurityExpressionRoot。相应方法的调用在基类中实现publicabstractclassSecurityExpressionRootimplementsSecurityExpressionOperations{privateStringdefaultRolePrefix="ROLE_";@OverridepublicfinalbooleanhasRole(Stringrole){returnhasAnyRole(role);}@Overridepublicfinalboolean(hasAnyRoleString...roles){returnhasAnyAuthorityName(this.defaultRolePrefix,roles);}@OverridepublicfinalbooleanhasAuthority(Stringauthority){returnhasAnyAuthority(authority);}@OverridepublicfinalbooleanhasAnyAuthority(String...authorities){returnhasAnyAuthorityName(null,authorities);}privatebooleanhasAnyAuthorityName(Stringprefix,String...roles){SetroleSet=getAuthoritySet();for(Stringrole:roles){//拼接ROLE_prefixStringdefaultedRole=getRoleWithDefaultPrefix(prefix,role);如果(roleSet.contains(defaultedRole)){返回真;}}返回假;}}通过上面的源码,无论是hasRole还是hasAuthority最终调用的是hasAnyAuthorityName方法,hasRole方法是用ROLE_前缀拼接的。总结:业务接口Controller权限控制方式多种多样。