写在前边整理的知识点都是从其他博客中来,如有侵权,立删!
参考:https://blog.csdn.net/qq_32867467/article/details/103097190
Spring Security 默认是禁用注解的,要想开启注解,要在继承WebSecurityConfigurerAdapter 的类加@EnableMethodSecurity 注解,并在该类中将AuthenticationManager 定义为Bean。
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
我们看到@EnableGlobalMethodSecurity 分别有prePostEnabled 、securedEnabled、jsr250Enabled 三个字段,其中每个字段代码一种注解支持,默认为false,true 为开启
/** 只要具有USER, ADMIN任意一种权限就可以访问 这里可以省略前缀ROLE_,实际的权限可能是ROLE_ADMIN */ @RolesAllowed({"USER", "ADMIN"})
可以使用@Secured 在方法上指定安全性要求 角色/权限等 只有对应 角色/权限 的用户才可以调用这些方法。 如果有人试图调用一个方法,但是不拥有所需的 角色/权限,那会将会拒绝访问将引发异常。
不支持Spring EL表达式
@Secured("ROLE_ADMIN") @Secured({ "ROLE_DBA", "ROLE_ADMIN" })
这个开启后支持Spring EL表达式 算是蛮厉害的。如果没有访问方法的权限,会抛AccessDeniedException
在方法执行之前执行,而且这里可以调用方法的参数,也可以得到参数值,这里利用JAVA8的参数名反射特性,如果没有JAVA8,那么也可以利用Spring Secuirty的@P标注参数,或利用Spring Data的@Param标注参数。
//无java8@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")voidchangePassword(@P("userId")long userId){}//有java8@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")voidchangePassword(long userId){}/** 这里表示在changePassword方法执行之前,判断方法参数userId的值是否等于principal中保存的当前用户的userId,或者当前用户是否具有ROLE_ADMIN权限,两种符合其一,就可以访问该 方法。 */
在方法执行之后执行,而且这里可以调用方法的返回值,如果EL为false,那么该方法也已经执行完了,可能会回滚。EL变量returnObject表示返回的对象。
@PostAuthorize("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')") UsergetUser();
在执行方法之后执行,而且这里可以调用方法的返回值,然后对返回值进行过滤或处理。EL变量returnObject表示返回的对象。只有方法返回的集合或数组类型的才可以使用。(与分页技术不兼容)
EL变量filterObject表示参数,如果有多个参数,可以使用@filterTarget注解参数,只有方法是集合或数组才行(与分页技术不兼容)。
interfaceTestPermissionEvaluator{booleancheck(Authentication authentication);}@Service("testPermissionEvaluator")publicclassTestPermissionEvaluatorImplimplementsTestPermissionEvaluator{publicbooleancheck(Authentication authentication){ System.out.println("进入了自定义的匹配器"+ authentication);returnfalse;}}// 返回true 就是有权限 false 则是无权限 。 然后在方法中这样玩即可@PreAuthorize("@testPermissionEvaluator.check(authentication)")public Stringtest0(){return"说明你有自定义权限";}
@Component@Slf4jpublicclassAccessDeniedAuthenticationHandlerimplementsAccessDeniedHandler{privatefinal ObjectMapper objectMapper;publicAccessDeniedAuthenticationHandler(ObjectMapper objectMapper){this.objectMapper= objectMapper;}@Overridepublicvoidhandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e)throws IOException{ log.info("没有权限"); httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); httpServletResponse.setContentType("application/json;charset=UTF-8"); httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e.getMessage()));}}
然后在WebSecurityConfig 中的 configure 中配置即可
http.anyRequest().authenticated().and().exceptionHandling().accessDeniedHandler(accessDeniedAuthenticationHandler);
权限表达式
表达式 | 说明 |
---|---|
hasRole([role]) | 用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀) |
hasAnyRole([role1,role2]) | 用户拥有任意一个制定的角色时返回true |
hasAuthority([authority]) | 等同于hasRole,但不会带有ROLE_前缀 |
asAnyAuthority([auth1,auth2]) | 等同于hasAnyRole |
permitAll | 永远返回true |
denyAll | 永远返回false |
authentication | 当前登录用户的authentication对象 |
fullAuthenticated | 当前用户既不是anonymous也不是rememberMe用户时返回true |
hasIpAddress(‘192.168.1.0/24’)) | 请求发送的IP匹配时返回true |
eg
@Overrideprotectedvoidconfigure(HttpSecurity http)throws Exception{ http.formLogin().failureHandler(failureAuthenticationHandler)// 自定义登录失败处理.successHandler(successAuthenticationHandler)// 自定义登录成功处理.and().logout().logoutUrl("/logout").and().formLogin().loginPage("/login").loginProcessingUrl("/authentication/form")// 自定义登录路径.and().authorizeRequests()// 对请求授权.antMatchers("/login","/authentication/require","/authentication/form").permitAll()// 这些页面不需要身份认证.antMatchers("/docker").hasRole("DOCKER").antMatchers("/java").hasRole("JAVA").antMatchers("/java").hasRole("JAVA").antMatchers("/custom").access("@testPermissionEvaluator.check(authentication)").anyRequest()//其他请求需要认证.authenticated().and().exceptionHandling().accessDeniedHandler(accessDeniedAuthenticationHandler).and().csrf().disable();// 禁用跨站攻击}
egaccess("hasRole('JAVA') or hasRole('DOCKER')")
上述示例代码中的access("@testPermissionEvaluator.check(authentication)")
的意思就是 去testPermissionEvaluator这个bean里来执行check方法,这里需要注意check 方法必须返回值是boolean
interfaceTestPermissionEvaluator{booleancheck(Authentication authentication);}@Service("testPermissionEvaluator")publicclassTestPermissionEvaluatorImplimplementsTestPermissionEvaluator{publicbooleancheck(Authentication authentication){//这里可以拿到登陆信息然后随便的去定制自己的权限 随便你怎么查询//true就是过,false就是不过 System.out.println("进入了自定义的匹配器"+ authentication);returnfalse;}}
示例项目地址: https://github.com/Blackcat308/reimagined-giggle or https://gitee.com/muncle/reimagined-giggle
也可以关注我的博客: https://blog.laocat.cn/