spring security url动态授权


spring security url动态授权

        

官网:https://docs.spring.io/spring-security/reference/servlet/appendix/faq.html#appendix-faq-dynamic-url-metadata

                        

                                        

引入jar包

              

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        ...
    </dependencies>

    ...

</project>

       

            

                                        

动态数据源

              

FilterInvocationSecurityMetadataSource:获取数据源

public interface FilterInvocationSecurityMetadataSource extends SecurityMetadataSource {
}

           

SecurityMetadataSource

public interface SecurityMetadataSource extends AopInfrastructureBean {
    Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException;
                                //获取object对应的权限

    Collection<ConfigAttribute> getAllConfigAttributes();

    boolean supports(Class<?> clazz);
}

            

ConfigAttribute:权限属性

public interface ConfigAttribute extends Serializable {
    String getAttribute();
}

             

             

 SecurityConfig:attrib可用来设置权限值(admin、user等)

public class SecurityConfig implements ConfigAttribute {
    private final String attrib;

    public SecurityConfig(String config) {
        Assert.hasText(config, "You must provide a configuration attribute");
        this.attrib = config;
    }

    public boolean equals(Object obj) {
        if (obj instanceof ConfigAttribute) {
            ConfigAttribute attr = (ConfigAttribute)obj;
            return this.attrib.equals(attr.getAttribute());
        } else {
            return false;
        }
    }

    public String getAttribute() {
        return this.attrib;
    }

    public int hashCode() {
        return this.attrib.hashCode();
    }

    public String toString() {
        return this.attrib;
    }

    public static List<ConfigAttribute> createListFromCommaDelimitedString(String access) {
        return createList(StringUtils.commaDelimitedListToStringArray(access));
    }

    public static List<ConfigAttribute> createList(String... attributeNames) {
        Assert.notNull(attributeNames, "You must supply an array of attribute names");
        List<ConfigAttribute> attributes = new ArrayList(attributeNames.length);
        String[] var2 = attributeNames;
        int var3 = attributeNames.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String attribute = var2[var4];
            attributes.add(new SecurityConfig(attribute.trim()));
        }

        return attributes;
    }
}

                

                     

                                        

权限拦截器

            

AbstractSecurityInterceptor

public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {
    protected final Log logger = LogFactory.getLog(this.getClass());
    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private ApplicationEventPublisher eventPublisher;
    private AccessDecisionManager accessDecisionManager;
    private AfterInvocationManager afterInvocationManager;
    private AuthenticationManager authenticationManager = new AbstractSecurityInterceptor.NoOpAuthenticationManager();
    private RunAsManager runAsManager = new NullRunAsManager();
    private boolean alwaysReauthenticate = false;
    private boolean rejectPublicInvocations = false;
    private boolean validateConfigAttributes = true;
    private boolean publishAuthorizationSuccess = false;

    public AbstractSecurityInterceptor() {
    }


    public void afterPropertiesSet() {
    public void setRunAsManager(RunAsManager runAsManager) {
    public void setMessageSource(MessageSource messageSource) {
    public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
    public void setAuthenticationManager(AuthenticationManager newManager) {
    public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
    public void setValidateConfigAttributes(boolean validateConfigAttributes) {
    public void setPublishAuthorizationSuccess(boolean publishAuthorizationSuccess) {
    public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
    public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {


    public boolean isAlwaysReauthenticate() {
    public boolean isRejectPublicInvocations() {
    public boolean isValidateConfigAttributes() {

    public AccessDecisionManager getAccessDecisionManager() {
    public AfterInvocationManager getAfterInvocationManager() {
    public AuthenticationManager getAuthenticationManager() {
    public RunAsManager getRunAsManager() {


    public abstract Class<?> getSecureObjectClass();
    public abstract SecurityMetadataSource obtainSecurityMetadataSource();

    protected void finallyInvocation(InterceptorStatusToken token) {
    protected InterceptorStatusToken beforeInvocation(Object object) {
    protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {


    private Authentication authenticateIfRequired() {
    private void publishEvent(ApplicationEvent event) {
    private void validateAttributeDefs(Collection<ConfigAttribute> attributeDefs) {
    private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes, Authentication authenticated) {
    private void credentialsNotFound(String reason, Object secureObject, Collection<ConfigAttribute> configAttribs) {


*********
静态内部类:NoOpAuthenticationManager

    private static class NoOpAuthenticationManager implements AuthenticationManager {
        private NoOpAuthenticationManager() {
        }

        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            throw new AuthenticationServiceException("Cannot authenticate " + authentication);
        }
    }
}

             

           

FilterSecurityInterceptor

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
    private boolean observeOncePerRequest = true;

    public FilterSecurityInterceptor() {
    }

    public void init(FilterConfig arg0) {
    }

    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        this.invoke(new FilterInvocation(request, response, chain));
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
        this.securityMetadataSource = newSource;
    }

    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
        if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
        } else {
            if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
                filterInvocation.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
            }

            InterceptorStatusToken token = super.beforeInvocation(filterInvocation);

            try {
                filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
            } finally {
                super.finallyInvocation(token);
            }

            super.afterInvocation(token, (Object)null);
        }
    }

    private boolean isApplied(FilterInvocation filterInvocation) {
        return filterInvocation.getRequest() != null && filterInvocation.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null;
    }

    public boolean isObserveOncePerRequest() {
        return this.observeOncePerRequest;
    }

    public void setObserveOncePerRequest(boolean observeOncePerRequest) {
        this.observeOncePerRequest = observeOncePerRequest;
    }
}

           

                          

                                        

认证管理器

            

AccessDecisionManager

public interface AccessDecisionManager {
    void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException;

    boolean supports(ConfigAttribute attribute);

    boolean supports(Class<?> clazz);
}

             

AbstractAccessDecisionManager

public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean, MessageSourceAware {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private List<AccessDecisionVoter<?>> decisionVoters;
    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private boolean allowIfAllAbstainDecisions = false;


    public boolean supports(ConfigAttribute attribute) {
        Iterator var2 = this.decisionVoters.iterator();

        AccessDecisionVoter voter;
        do {
            if (!var2.hasNext()) {
                return false;
            }

            voter = (AccessDecisionVoter)var2.next();
        } while(!voter.supports(attribute));

        return true;
    }

    public boolean supports(Class<?> clazz) {
        Iterator var2 = this.decisionVoters.iterator();

        AccessDecisionVoter voter;
        do {
            if (!var2.hasNext()) {
                return true;
            }

            voter = (AccessDecisionVoter)var2.next();
        } while(voter.supports(clazz));

        return false;
    }


    public void setMessageSource(MessageSource messageSource) {
    public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) {

    public List<AccessDecisionVoter<?>> getDecisionVoters() {
    public boolean isAllowIfAllAbstainDecisions() {

    public String toString() {
    public void afterPropertiesSet() {

    protected final void checkAllowIfAllAbstainDecisions() {
    protected AbstractAccessDecisionManager(List<AccessDecisionVoter<?>> decisionVoters) {

             

                

AffirmativedBased

public class AffirmativeBased extends AbstractAccessDecisionManager {
    public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
        super(decisionVoters);
    }

    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
        int deny = 0;
        Iterator var5 = this.getDecisionVoters().iterator();

        while(var5.hasNext()) {
            AccessDecisionVoter voter = (AccessDecisionVoter)var5.next();
            int result = voter.vote(authentication, object, configAttributes);
            switch(result) {
            case -1:
                ++deny;
                break;
            case 1:
                return;
            }
        }

        if (deny > 0) {
            throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
        } else {
            this.checkAllowIfAllAbstainDecisions();
        }
    }
}

             

AccessDecisionVoter

public interface AccessDecisionVoter<S> {
    int ACCESS_GRANTED = 1;
    int ACCESS_ABSTAIN = 0;
    int ACCESS_DENIED = -1;

    boolean supports(ConfigAttribute attribute);

    boolean supports(Class<?> clazz);

    int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
}

             

          

 RoleVoter:根据权限判断是否让请求通过

public class RoleVoter implements AccessDecisionVoter<Object> {
    private String rolePrefix = "ROLE_";

    public RoleVoter() {
    }

    public String getRolePrefix() {
        return this.rolePrefix;
    }

    public void setRolePrefix(String rolePrefix) {
        this.rolePrefix = rolePrefix;
    }

    public boolean supports(ConfigAttribute attribute) {
        return attribute.getAttribute() != null && attribute.getAttribute().startsWith(this.getRolePrefix());
    }

    public boolean supports(Class<?> clazz) {
        return true;
    }

    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        if (authentication == null) {
            return -1;
        } else {
            int result = 0;
            Collection<? extends GrantedAuthority> authorities = this.extractAuthorities(authentication);
            Iterator var6 = attributes.iterator();

            while(true) {
                ConfigAttribute attribute;
                do {
                    if (!var6.hasNext()) {
                        return result;
                    }

                    attribute = (ConfigAttribute)var6.next();
                } while(!this.supports(attribute));

                result = -1;
                Iterator var8 = authorities.iterator();

                while(var8.hasNext()) {
                    GrantedAuthority authority = (GrantedAuthority)var8.next();
                    if (attribute.getAttribute().equals(authority.getAuthority())) {
                        return 1;
                    }
                }
            }
        }
    }

    Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {
        return authentication.getAuthorities();
    }
}

         

             

                                        

使用示例

            

                            

         

AuthorityDto

@Data
public class AuthorityDto {

    private String path;
    private List<String> authorities;
}

          

SecurityDataSourceUtil

public class SecurityDataSourceUtil {

    public static Map<String, Collection<ConfigAttribute>> requestAuthoritiesMap = new HashMap<>();

    public static void setAuthorities(String path, Collection<ConfigAttribute> configAttributes){
        requestAuthoritiesMap.put(path, configAttributes);
    }

    public static Collection<ConfigAttribute> getAuthorities(String path){
        return requestAuthoritiesMap.get(path);
    }
}

           

CustomFilterSecurityMetadataSource

@Component
public class CustomFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        Set<ConfigAttribute> configAttributes = new HashSet<>();
        for (Collection<ConfigAttribute> item : SecurityDataSourceUtil.requestAuthoritiesMap.values()){
            configAttributes.addAll(item);
        }

        return configAttributes;
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        HttpServletRequest request = ((FilterInvocation)object).getRequest();

        for (Map.Entry<String, Collection<ConfigAttribute>> entry: SecurityDataSourceUtil.requestAuthoritiesMap.entrySet()){
            if (new AntPathRequestMatcher(entry.getKey()).matches(request)) {
                return entry.getValue();
            }
        }

        return null;
    }
}

         

CustomUrlInterceptor

@Data
@Component
@EqualsAndHashCode(callSuper = true)
public class CustomUrlInterceptor extends AbstractSecurityInterceptor implements Filter, InitializingBean {

    private static final String FILTER_APPLIED = "__spring_security_custom_filterSecurityInterceptor_filterApplied";
    private boolean observeOncePerRequest = true;

    @Resource
    private FilterInvocationSecurityMetadataSource securityMetadataSource;


    public CustomUrlInterceptor() {
    }

    @Override
    public void afterPropertiesSet() {
        List<AccessDecisionVoter<?>> accessDecisionVoters = new ArrayList<>();
        accessDecisionVoters.add(new RoleVoter());

        AccessDecisionManager accessDecisionManager = new AffirmativeBased(accessDecisionVoters);
        this.setAccessDecisionManager(accessDecisionManager);
    }

    public void init(FilterConfig arg0) {
    }

    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        this.invoke(new FilterInvocation(request, response, chain));
    }

    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
        if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
        } else {
            if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
                filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }

            InterceptorStatusToken token = super.beforeInvocation(filterInvocation);

            try {
                filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
            } finally {
                super.finallyInvocation(token);
            }

            super.afterInvocation(token, null);
        }
    }

    private boolean isApplied(FilterInvocation filterInvocation) {
        return filterInvocation.getRequest() != null && filterInvocation.getRequest().getAttribute(FILTER_APPLIED) != null;
    }

    public boolean isObserveOncePerRequest() {
        return this.observeOncePerRequest;
    }

    public void setObserveOncePerRequest(boolean observeOncePerRequest) {
        this.observeOncePerRequest = observeOncePerRequest;
    }
}

             

WebSecurity

@Configuration
public class WebSecurity {

    @Bean
    public PasswordEncoder initPasswordEncoder(){
        return new Pbkdf2PasswordEncoder();
    }
}

           

WebSecurityConfig

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private CustomUrlInterceptor customUrlInterceptor;

    @Bean
    public SecurityFilterChain initSecurityFilterChain(HttpSecurity http) throws Exception{
        http.addFilterAfter(customUrlInterceptor, FilterSecurityInterceptor.class);
        http.csrf().disable();

        return http.formLogin().and().authorizeRequests()
                .antMatchers("/authority").permitAll()
                .antMatchers("/hello").hasRole("admin")
                .anyRequest().authenticated()
                .and().build();
    }

    @Bean
    public UserDetailsService initUserDetailsService(){
        UserDetails userDetails = User.withUsername("gtlx").password(passwordEncoder.encode("123456")).roles("admin").build();

        return new InMemoryUserDetailsManager(userDetails);
    }
}

           

HelloController

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello(){
        return "hello";
    }

    @RequestMapping("/hello2")
    public String hello2(){
        return "hello2";
    }
}

            

AuthorityController

@RestController
public class AuthorityController {

    @RequestMapping("/authority")
    public String authority(@RequestBody AuthorityDto authorityDto){
        List<ConfigAttribute> configAttributes = new ArrayList<>();
        for (String s: authorityDto.getAuthorities()){
            SecurityConfig securityConfig = new SecurityConfig("ROLE_"+s);
            configAttributes.add(securityConfig);
        }
        SecurityDataSourceUtil.setAuthorities(authorityDto.getPath(), configAttributes);

        return "success";
    }
}

          

                  

                                        

使用测试

            

localhost:8080/hello,输入密码

            

            

            

localhost:8080/hello2

            

            

localhost:8080/authority,路径/hello2添加权限认证

            

            

localhost:8080/hello2,当前用户没有user权限,禁止访问

            

            

localhost:8080/authority,修改路径权限为admin

            

            

localhost:8080/hello2,当前用户有admin权限,可正常访问

            

                 

                              

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值