🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
资源的访问权限都是放在数据库当中的,不可能是放在代码中,不然得有多少个`antMatchers(...).hasAuthority(...)`。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { ... @Override protected void configure(HttpSecurity http) throws Exception { ... http.authorizeRequests() .antMatchers("/layui/**", "/to/login") .permitAll() //当用户有admin权限时才能访问/account/message .antMatchers("/account/message").hasAuthority("admin") ... } } ``` <br/> 下面实现访问资源`/account/message`时去数据库查询该资源所需的权限,具备相应的权限才能访问。步骤如下: **1. 根据访问的URL去数据库查询该URL所需要的权限** ```java @Component public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Autowired private SysPermissionService sysPermissionService; @Override public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException { //获取请求地址 String url = ((FilterInvocation) o).getRequestUrl(); //去数据库查询访问该url所需要的权限 List<SysPermission> permissions = sysPermissionService.findByUrl(url); if(CollectionUtils.isEmpty(permissions)) { //返回null则表示无需任何权限即可访问,即使没有登录 return null; } String[] attrs = new String[permissions.size()]; for (int i = 0; i < permissions.size(); i++) { attrs[i] = permissions.get(i).getPermission(); } return SecurityConfig.createList(attrs); } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> aClass) { return true; } } ``` **2. 拦截当前请求** AbstractSecurityInterceptor 拦截当前请求并获取相关权限组成权限列表,将权限列表交给接口 AccessDecisionManager 做下一步处理。 ```java @Component public class CustomAbstractSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { @Autowired private FilterInvocationSecurityMetadataSource securityMetadataSource; @Autowired public void setMyAccessDecisionManager(CustomAccessDecisionManager accessDecisionManager) { super.setAccessDecisionManager(accessDecisionManager); } @Override public Class<?> getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain); invoke(fi); } public void invoke(FilterInvocation fi) throws IOException, ServletException { //拦截当前请求的url,并判断是否具备相应的权限 InterceptorStatusToken token = super.beforeInvocation(fi); try { //执行下一个拦截器 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } } ``` **3. 实现接口AccessDecisionManager来判断对当前请求是否放行** 如果有访问当前资源的权限则放行,否则抛出异常。 ```java @Component public class CustomAccessDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException { Iterator<ConfigAttribute> iterator = collection.iterator(); while (iterator.hasNext()) { ConfigAttribute ca = iterator.next(); //当前请求需要的权限 String needRole = ca.getAttribute(); //当前用户所具有的权限 Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for (GrantedAuthority authority : authorities) { if (authority.getAuthority().equals(needRole)) { return; } } } throw new AccessDeniedException("权限不足!"); } @Override public boolean supports(ConfigAttribute configAttribute) { return true; } @Override public boolean supports(Class<?> aClass) { return true; } } ``` **4. 将上面的组件注册到环境中** ```java @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .... @Autowired private CustomAbstractSecurityInterceptor securityInterceptor; @Autowired private CustomAccessDecisionManager accessDecisionManager; @Autowired private CustomFilterInvocationSecurityMetadataSource securityMetadataSource; ... @Override protected void configure(HttpSecurity http) throws Exception { ... http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O o) { o.setAccessDecisionManager(accessDecisionManager); o.setSecurityMetadataSource(securityMetadataSource); return o; } }); http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class); ... } ... } ``` **5. 测试结果** 当有权限时正常访问,无权限时返回 Forbidden 提醒。 ```json { "timestamp":"2022-07-29T03:30:32.813+00:00", "status":403, "error":"Forbidden", "message":"Forbidden", "path":"/account/message" } ```