• 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

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0modelVersion>
    5. <parent>
    6. <groupId>org.springframework.bootgroupId>
    7. <artifactId>spring-boot-starter-parentartifactId>
    8. <version>2.7.3version>
    9. <relativePath/>
    10. parent>
    11. <groupId>com.examplegroupId>
    12. <artifactId>demoartifactId>
    13. <version>0.0.1-SNAPSHOTversion>
    14. <name>demoname>
    15. <description>Demo project for Spring Bootdescription>
    16. <properties>
    17. <java.version>1.8java.version>
    18. properties>
    19. <dependencies>
    20. <dependency>
    21. <groupId>org.springframework.bootgroupId>
    22. <artifactId>spring-boot-starter-securityartifactId>
    23. dependency>
    24. ...
    25. dependencies>
    26. ...
    27. project>

           

                

                                            

    动态数据源

                  

    FilterInvocationSecurityMetadataSource:获取数据源

    1. public interface FilterInvocationSecurityMetadataSource extends SecurityMetadataSource {
    2. }

               

    SecurityMetadataSource

    1. public interface SecurityMetadataSource extends AopInfrastructureBean {
    2. Collection getAttributes(Object object) throws IllegalArgumentException;
    3. //获取object对应的权限
    4. Collection getAllConfigAttributes();
    5. boolean supports(Class clazz);
    6. }

                

    ConfigAttribute:权限属性

    1. public interface ConfigAttribute extends Serializable {
    2. String getAttribute();
    3. }

                 

                 

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

    1. public class SecurityConfig implements ConfigAttribute {
    2. private final String attrib;
    3. public SecurityConfig(String config) {
    4. Assert.hasText(config, "You must provide a configuration attribute");
    5. this.attrib = config;
    6. }
    7. public boolean equals(Object obj) {
    8. if (obj instanceof ConfigAttribute) {
    9. ConfigAttribute attr = (ConfigAttribute)obj;
    10. return this.attrib.equals(attr.getAttribute());
    11. } else {
    12. return false;
    13. }
    14. }
    15. public String getAttribute() {
    16. return this.attrib;
    17. }
    18. public int hashCode() {
    19. return this.attrib.hashCode();
    20. }
    21. public String toString() {
    22. return this.attrib;
    23. }
    24. public static List createListFromCommaDelimitedString(String access) {
    25. return createList(StringUtils.commaDelimitedListToStringArray(access));
    26. }
    27. public static List createList(String... attributeNames) {
    28. Assert.notNull(attributeNames, "You must supply an array of attribute names");
    29. List attributes = new ArrayList(attributeNames.length);
    30. String[] var2 = attributeNames;
    31. int var3 = attributeNames.length;
    32. for(int var4 = 0; var4 < var3; ++var4) {
    33. String attribute = var2[var4];
    34. attributes.add(new SecurityConfig(attribute.trim()));
    35. }
    36. return attributes;
    37. }
    38. }

                    

                         

                                            

    权限拦截器

                

    AbstractSecurityInterceptor

    1. public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {
    2. protected final Log logger = LogFactory.getLog(this.getClass());
    3. protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    4. private ApplicationEventPublisher eventPublisher;
    5. private AccessDecisionManager accessDecisionManager;
    6. private AfterInvocationManager afterInvocationManager;
    7. private AuthenticationManager authenticationManager = new AbstractSecurityInterceptor.NoOpAuthenticationManager();
    8. private RunAsManager runAsManager = new NullRunAsManager();
    9. private boolean alwaysReauthenticate = false;
    10. private boolean rejectPublicInvocations = false;
    11. private boolean validateConfigAttributes = true;
    12. private boolean publishAuthorizationSuccess = false;
    13. public AbstractSecurityInterceptor() {
    14. }
    15. public void afterPropertiesSet() {
    16. public void setRunAsManager(RunAsManager runAsManager) {
    17. public void setMessageSource(MessageSource messageSource) {
    18. public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
    19. public void setAuthenticationManager(AuthenticationManager newManager) {
    20. public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
    21. public void setValidateConfigAttributes(boolean validateConfigAttributes) {
    22. public void setPublishAuthorizationSuccess(boolean publishAuthorizationSuccess) {
    23. public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
    24. public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
    25. public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    26. public boolean isAlwaysReauthenticate() {
    27. public boolean isRejectPublicInvocations() {
    28. public boolean isValidateConfigAttributes() {
    29. public AccessDecisionManager getAccessDecisionManager() {
    30. public AfterInvocationManager getAfterInvocationManager() {
    31. public AuthenticationManager getAuthenticationManager() {
    32. public RunAsManager getRunAsManager() {
    33. public abstract Class getSecureObjectClass();
    34. public abstract SecurityMetadataSource obtainSecurityMetadataSource();
    35. protected void finallyInvocation(InterceptorStatusToken token) {
    36. protected InterceptorStatusToken beforeInvocation(Object object) {
    37. protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
    38. private Authentication authenticateIfRequired() {
    39. private void publishEvent(ApplicationEvent event) {
    40. private void validateAttributeDefs(Collection attributeDefs) {
    41. private void attemptAuthorization(Object object, Collection attributes, Authentication authenticated) {
    42. private void credentialsNotFound(String reason, Object secureObject, Collection configAttribs) {
    43. *********
    44. 静态内部类:NoOpAuthenticationManager
    45. private static class NoOpAuthenticationManager implements AuthenticationManager {
    46. private NoOpAuthenticationManager() {
    47. }
    48. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    49. throw new AuthenticationServiceException("Cannot authenticate " + authentication);
    50. }
    51. }
    52. }

                 

               

    FilterSecurityInterceptor

    1. public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    2. private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
    3. private FilterInvocationSecurityMetadataSource securityMetadataSource;
    4. private boolean observeOncePerRequest = true;
    5. public FilterSecurityInterceptor() {
    6. }
    7. public void init(FilterConfig arg0) {
    8. }
    9. public void destroy() {
    10. }
    11. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    12. this.invoke(new FilterInvocation(request, response, chain));
    13. }
    14. public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
    15. return this.securityMetadataSource;
    16. }
    17. public SecurityMetadataSource obtainSecurityMetadataSource() {
    18. return this.securityMetadataSource;
    19. }
    20. public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
    21. this.securityMetadataSource = newSource;
    22. }
    23. public Class getSecureObjectClass() {
    24. return FilterInvocation.class;
    25. }
    26. public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
    27. if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {
    28. filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
    29. } else {
    30. if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
    31. filterInvocation.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
    32. }
    33. InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
    34. try {
    35. filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
    36. } finally {
    37. super.finallyInvocation(token);
    38. }
    39. super.afterInvocation(token, (Object)null);
    40. }
    41. }
    42. private boolean isApplied(FilterInvocation filterInvocation) {
    43. return filterInvocation.getRequest() != null && filterInvocation.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null;
    44. }
    45. public boolean isObserveOncePerRequest() {
    46. return this.observeOncePerRequest;
    47. }
    48. public void setObserveOncePerRequest(boolean observeOncePerRequest) {
    49. this.observeOncePerRequest = observeOncePerRequest;
    50. }
    51. }

               

                              

                                            

    认证管理器

                

    AccessDecisionManager

    1. public interface AccessDecisionManager {
    2. void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException;
    3. boolean supports(ConfigAttribute attribute);
    4. boolean supports(Class clazz);
    5. }

                 

    AbstractAccessDecisionManager

    1. public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean, MessageSourceAware {
    2. protected final Log logger = LogFactory.getLog(this.getClass());
    3. private List> decisionVoters;
    4. protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    5. private boolean allowIfAllAbstainDecisions = false;
    6. public boolean supports(ConfigAttribute attribute) {
    7. Iterator var2 = this.decisionVoters.iterator();
    8. AccessDecisionVoter voter;
    9. do {
    10. if (!var2.hasNext()) {
    11. return false;
    12. }
    13. voter = (AccessDecisionVoter)var2.next();
    14. } while(!voter.supports(attribute));
    15. return true;
    16. }
    17. public boolean supports(Class clazz) {
    18. Iterator var2 = this.decisionVoters.iterator();
    19. AccessDecisionVoter voter;
    20. do {
    21. if (!var2.hasNext()) {
    22. return true;
    23. }
    24. voter = (AccessDecisionVoter)var2.next();
    25. } while(voter.supports(clazz));
    26. return false;
    27. }
    28. public void setMessageSource(MessageSource messageSource) {
    29. public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) {
    30. public List> getDecisionVoters() {
    31. public boolean isAllowIfAllAbstainDecisions() {
    32. public String toString() {
    33. public void afterPropertiesSet() {
    34. protected final void checkAllowIfAllAbstainDecisions() {
    35. protected AbstractAccessDecisionManager(List> decisionVoters) {

                 

                    

    AffirmativedBased

    1. public class AffirmativeBased extends AbstractAccessDecisionManager {
    2. public AffirmativeBased(List> decisionVoters) {
    3. super(decisionVoters);
    4. }
    5. public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException {
    6. int deny = 0;
    7. Iterator var5 = this.getDecisionVoters().iterator();
    8. while(var5.hasNext()) {
    9. AccessDecisionVoter voter = (AccessDecisionVoter)var5.next();
    10. int result = voter.vote(authentication, object, configAttributes);
    11. switch(result) {
    12. case -1:
    13. ++deny;
    14. break;
    15. case 1:
    16. return;
    17. }
    18. }
    19. if (deny > 0) {
    20. throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
    21. } else {
    22. this.checkAllowIfAllAbstainDecisions();
    23. }
    24. }
    25. }

                 

    AccessDecisionVoter

    1. public interface AccessDecisionVoter {
    2. int ACCESS_GRANTED = 1;
    3. int ACCESS_ABSTAIN = 0;
    4. int ACCESS_DENIED = -1;
    5. boolean supports(ConfigAttribute attribute);
    6. boolean supports(Class clazz);
    7. int vote(Authentication authentication, S object, Collection attributes);
    8. }

                 

              

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

    1. public class RoleVoter implements AccessDecisionVoter {
    2. private String rolePrefix = "ROLE_";
    3. public RoleVoter() {
    4. }
    5. public String getRolePrefix() {
    6. return this.rolePrefix;
    7. }
    8. public void setRolePrefix(String rolePrefix) {
    9. this.rolePrefix = rolePrefix;
    10. }
    11. public boolean supports(ConfigAttribute attribute) {
    12. return attribute.getAttribute() != null && attribute.getAttribute().startsWith(this.getRolePrefix());
    13. }
    14. public boolean supports(Class clazz) {
    15. return true;
    16. }
    17. public int vote(Authentication authentication, Object object, Collection attributes) {
    18. if (authentication == null) {
    19. return -1;
    20. } else {
    21. int result = 0;
    22. Collectionextends GrantedAuthority> authorities = this.extractAuthorities(authentication);
    23. Iterator var6 = attributes.iterator();
    24. while(true) {
    25. ConfigAttribute attribute;
    26. do {
    27. if (!var6.hasNext()) {
    28. return result;
    29. }
    30. attribute = (ConfigAttribute)var6.next();
    31. } while(!this.supports(attribute));
    32. result = -1;
    33. Iterator var8 = authorities.iterator();
    34. while(var8.hasNext()) {
    35. GrantedAuthority authority = (GrantedAuthority)var8.next();
    36. if (attribute.getAttribute().equals(authority.getAuthority())) {
    37. return 1;
    38. }
    39. }
    40. }
    41. }
    42. }
    43. Collectionextends GrantedAuthority> extractAuthorities(Authentication authentication) {
    44. return authentication.getAuthorities();
    45. }
    46. }
    47.          

                   

                                              

      使用示例

                  

                                  

               

      AuthorityDto

      1. @Data
      2. public class AuthorityDto {
      3. private String path;
      4. private List authorities;
      5. }

                

      SecurityDataSourceUtil

      1. public class SecurityDataSourceUtil {
      2. public static Map> requestAuthoritiesMap = new HashMap<>();
      3. public static void setAuthorities(String path, Collection configAttributes){
      4. requestAuthoritiesMap.put(path, configAttributes);
      5. }
      6. public static Collection getAuthorities(String path){
      7. return requestAuthoritiesMap.get(path);
      8. }
      9. }

                 

      CustomFilterSecurityMetadataSource

      1. @Component
      2. public class CustomFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
      3. @Override
      4. public boolean supports(Class clazz) {
      5. return FilterInvocation.class.isAssignableFrom(clazz);
      6. }
      7. @Override
      8. public Collection getAllConfigAttributes() {
      9. Set configAttributes = new HashSet<>();
      10. for (Collection item : SecurityDataSourceUtil.requestAuthoritiesMap.values()){
      11. configAttributes.addAll(item);
      12. }
      13. return configAttributes;
      14. }
      15. @Override
      16. public Collection getAttributes(Object object) throws IllegalArgumentException {
      17. HttpServletRequest request = ((FilterInvocation)object).getRequest();
      18. for (Map.Entry> entry: SecurityDataSourceUtil.requestAuthoritiesMap.entrySet()){
      19. if (new AntPathRequestMatcher(entry.getKey()).matches(request)) {
      20. return entry.getValue();
      21. }
      22. }
      23. return null;
      24. }
      25. }

               

      CustomUrlInterceptor

      1. @Data
      2. @Component
      3. @EqualsAndHashCode(callSuper = true)
      4. public class CustomUrlInterceptor extends AbstractSecurityInterceptor implements Filter, InitializingBean {
      5. private static final String FILTER_APPLIED = "__spring_security_custom_filterSecurityInterceptor_filterApplied";
      6. private boolean observeOncePerRequest = true;
      7. @Resource
      8. private FilterInvocationSecurityMetadataSource securityMetadataSource;
      9. public CustomUrlInterceptor() {
      10. }
      11. @Override
      12. public void afterPropertiesSet() {
      13. List> accessDecisionVoters = new ArrayList<>();
      14. accessDecisionVoters.add(new RoleVoter());
      15. AccessDecisionManager accessDecisionManager = new AffirmativeBased(accessDecisionVoters);
      16. this.setAccessDecisionManager(accessDecisionManager);
      17. }
      18. public void init(FilterConfig arg0) {
      19. }
      20. public void destroy() {
      21. }
      22. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      23. this.invoke(new FilterInvocation(request, response, chain));
      24. }
      25. public SecurityMetadataSource obtainSecurityMetadataSource() {
      26. return this.securityMetadataSource;
      27. }
      28. public Class getSecureObjectClass() {
      29. return FilterInvocation.class;
      30. }
      31. public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
      32. if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {
      33. filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
      34. } else {
      35. if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
      36. filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
      37. }
      38. InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
      39. try {
      40. filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
      41. } finally {
      42. super.finallyInvocation(token);
      43. }
      44. super.afterInvocation(token, null);
      45. }
      46. }
      47. private boolean isApplied(FilterInvocation filterInvocation) {
      48. return filterInvocation.getRequest() != null && filterInvocation.getRequest().getAttribute(FILTER_APPLIED) != null;
      49. }
      50. public boolean isObserveOncePerRequest() {
      51. return this.observeOncePerRequest;
      52. }
      53. public void setObserveOncePerRequest(boolean observeOncePerRequest) {
      54. this.observeOncePerRequest = observeOncePerRequest;
      55. }
      56. }

                   

      WebSecurity

      1. @Configuration
      2. public class WebSecurity {
      3. @Bean
      4. public PasswordEncoder initPasswordEncoder(){
      5. return new Pbkdf2PasswordEncoder();
      6. }
      7. }

                 

      WebSecurityConfig

      1. @Configuration
      2. @EnableWebSecurity
      3. public class WebSecurityConfig {
      4. @Resource
      5. private PasswordEncoder passwordEncoder;
      6. @Resource
      7. private CustomUrlInterceptor customUrlInterceptor;
      8. @Bean
      9. public SecurityFilterChain initSecurityFilterChain(HttpSecurity http) throws Exception{
      10. http.addFilterAfter(customUrlInterceptor, FilterSecurityInterceptor.class);
      11. http.csrf().disable();
      12. return http.formLogin().and().authorizeRequests()
      13. .antMatchers("/authority").permitAll()
      14. .antMatchers("/hello").hasRole("admin")
      15. .anyRequest().authenticated()
      16. .and().build();
      17. }
      18. @Bean
      19. public UserDetailsService initUserDetailsService(){
      20. UserDetails userDetails = User.withUsername("gtlx").password(passwordEncoder.encode("123456")).roles("admin").build();
      21. return new InMemoryUserDetailsManager(userDetails);
      22. }
      23. }

                 

      HelloController

      1. @RestController
      2. public class HelloController {
      3. @RequestMapping("/hello")
      4. public String hello(){
      5. return "hello";
      6. }
      7. @RequestMapping("/hello2")
      8. public String hello2(){
      9. return "hello2";
      10. }
      11. }

                  

      AuthorityController

      1. @RestController
      2. public class AuthorityController {
      3. @RequestMapping("/authority")
      4. public String authority(@RequestBody AuthorityDto authorityDto){
      5. List configAttributes = new ArrayList<>();
      6. for (String s: authorityDto.getAuthorities()){
      7. SecurityConfig securityConfig = new SecurityConfig("ROLE_"+s);
      8. configAttributes.add(securityConfig);
      9. }
      10. SecurityDataSourceUtil.setAuthorities(authorityDto.getPath(), configAttributes);
      11. return "success";
      12. }
      13. }

                

                        

                                              

      使用测试

                  

      localhost:8080/hello,输入密码

                  

                  

                  

      localhost:8080/hello2

                  

                  

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

                  

                  

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

                  

                  

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

                  

                  

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

                  

                       

                                    

    48. 相关阅读:
      基于WebRTC构建的程序因虚拟内存不足导致闪退问题的排查以及解决办法的探究
      长列表优化:用 React 实现虚拟列表
      tensorflow学习率指数衰减ExponentialDecay的参数介绍与使用方法
      细说 PDF 里的“秘密”——如何标记密文
      心律守护 基于机器学习的心脏病预测
      蔚来杯_2022牛客暑期多校训练营(加赛) E.Everyone is bot
      jQuery常用API--样式操作
      创建线程池执行java代码逻辑
      记录一次 添加脚本的记录+改错记录
      Java中的数组、Set、List、Map类型的互相转换总结
    49. 原文地址:https://blog.csdn.net/weixin_43931625/article/details/126917739