Spring Security(1)

Spring Security的基于内存身份验证主要流程

        1.请求被(身份验证过滤器)拦截 2.身份验证职能被委托给(身份验证管理器AuthenticationManager)3.身份验证管理器使用实现身份验证逻辑的(身份验证提供程序DaoAuthenticationProvider)4.身份验证提供程序使用(用户详情服务UserDetailsService)找到用户,并使用(密码编码器NoOpPasswordEncoder)验证密码,5.身份验证的结果返回给过滤器6.已验证实体的详情被储存到安全上下文中。

        a.在springboot启动过滤器配置的ApplicationFilterChain中ApplicationFilterConfig[] filters,filters[4]值为springSecurityFilterChain,将其委托给FilterChainProxy类

        b.FilterChainProxy类中List<SecurityFilterChain> filterChains,filterChains[0]值为DefaultSecurityFilterChain。DefaultSecurityFilterChain类中List<Filter> filters值为12个主要的过滤器,其中包含BasicAuthenticationFilter过滤器

        c.BasicAuthenticationFilter类中有重要的身份认证管理器AuthenticationManager authenticationManager值为ProviderManager类

        d.ProviderManager类有两个重要的属性List<AuthenticationProvider> providers 值为AnonymousAuthenticationProvider,AuthenticationManager parent值为ProviderManager类。在parent中的parent为null,providers有一个DaoAuthenticationProvider。

        e.DaoAuthenticationProvider类中有两个重要属性PasswordEncoder passwordEncoder=NoOpPasswordEncoder

,UserDetailsService userDetailsService=InMemoryUserDetailsManager,调用Authentication authenticate(Authentication authentication)

        f.DaoAuthenticationProvider.authenticate(authentication)方法:将前端传来的用户名,密码初始化为Authentication的实例(authenticated = false)UsernamePasswordAuthenticationToken对象作为入参,返回(authenticated = false)的对象。首先在retrieveUser()中this.InMemoryUserDetailsManager.loadUserByUsername(name)返回内存中的该用户详情,在additionalAuthenticationChecks()方法this.passwordEncoder.matches() 验证密码。

//1. BasicAuthenticationFilter.doFilterInternal()   
String[] tokens = this.extractAndDecodeHeader(...)
String username = tokens[0];
UsernamePasswordAuthenticationToken authRequest = new 
                 UsernamePasswordAuthenticationToken(username, tokens[1]);

//2.BasicAuthenticationFilter.authenticationManager=ProviderManager.class
//  ProviderManager类
//    属性List<AuthenticationProvider> providers;              
//        值providers[0]=AnonymousAuthenticationProvider
//    属性private AuthenticationManager parent;
//        值parent=ProviderManager类
//          属性List<AuthenticationProvider> providers;
//              值providers为null
//          属性private AuthenticationManager parent;
//              值parent=DaoAuthenticationProvider
Authentication authResult = this.authenticationManager.authenticate(authRequest)->{
    
    //3.调用ProviderManager.providers循环调用过程中
    // result不为null,则做处理后返回。
    result = providers.authenticate(authentication)—>{
        // 接口AuthenticationProvide.authenticate(Authentication var1)|supports()
        // 实现类DaoAuthenticationProvider
        // 实现类AnonymousAuthenticationProvider
        //   属性String key
    
        // 只有authentication为AnonymousAuthenticationToken及子类,才能返回ture
        // AnonymousAuthenticationToken.class.isAssignableFrom(authentication);

        if (!this.supports(authentication.getClass())) {
            return null;
        // 如果传入的authentication.getKeyHash()与this.key.hashCode()值不相同,则
        //  抛出异常BadCredentialsException
        } else if (this.key.hashCode() != 
               ((AnonymousAuthenticationToken)authentication).getKeyHash()) {
            throw new BadCredentialsException();
        // 相同则返回对象
        } else {
            return authentication;
        }
        
    };
    
    // DaoAuthenticationProvider类
    //   属性UserDetailsChecker preAuthenticationChecks
    //   属性UserDetailsChecker postAuthenticationChecks
    // 接口UserDetailsChecker.check(UserDetails var1)
    //   实现类DefaultPreAuthenticationChecks|DefaultPostAuthenticationChecks
    //   属性PasswordEncoder passwordEncoder;
    //   属性UserDetailsService userDetailsService;
    // 接口UserDetailsService.loadUserByUsername()
    //   实现类InMemoryUserDetailsManager 内存储存用户详情
    //   实现类JdbcUserDetailsManager 数据库储存用户详情
    //       两个实现类有公有接口UserDetailsManager.createUser()|updateUser|deleteUser()
    //                          | changePassword() |userExists()   

    // result为null且parent不为null,则
    // 调用ProviderManager.parent.authenticate
    // 当result不为null时,返回。结果为null,抛出异常
    result = this.parent.authenticate(authentication)—>{
         String username = authentication.getPrincipal();
         // 根据username,从缓存中取出user
         boolean cacheWasUsed = true;
         // 接口UserCache.getUserFromCache()|putUserInCache()|removeUserFromCache()
         // 实现类SpringCacheBasedUserCache
         UserDetails user = this.userCache.getUserFromCache(username);
         // 如果缓存为null
         if (user == null) {
            cacheWasUsed = false;
            try {
                // 从内存或数据库中,通过username找到user
                user = this.retrieveUser(username, 
                       (UsernamePasswordAuthenticationToken)authentication)->{
                    // userNotFoundEncodedPassword为null时,则
                    //      调用this.passwordEncoder.encode("userNotFoundPassword");
                    prepareTimingAttackProtection();

                    // 从JdbcUserDetailsManager或InMemoryUserDetailsManager
                    // 检索User
                    UserDetails loadedUser = this.getUserDetailsService() 
                                                  .loadUserByUsername(username)—>{
                         // InMemoryUserDetailsManager 的实现
                         UserDetails user =                                         
                                 (UserDetails)this.users.get(username.toLowerCase());
                         if (user == null) {
                            throw new UsernameNotFoundException(username);
                         } else {
                            return new User(user.getUsername(), user.getPassword(), 
                                   user.isEnabled(), user.isAccountNonExpired(), 
                                        user.isCredentialsNonExpired(), 
                                user.isAccountNonLocked(), user.getAuthorities());
                         }
                        
                         // JdbcUserDetailsManager 的实现
                         ...
                    };
                    if (loadedUser == null) {
                        throw new InternalAuthenticationServiceException();
                    } else {
                        return loadedUser;
                    }
                };
            }catch(UsernameNotFoundException var6){
                
                if (this.hideUserNotFoundExceptions) {
                    // 隐藏UsernameNotFoundException异常,抛出BadCredentialsException
                    throw new BadCredentialsException(...));
                }
                //抛出UsernameNotFoundException异常
                throw var6;
            }   
         }
         
         
         //缓存不为null
         try{
            // 检查账户是否被locked|disabled|expired
            this.preAuthenticationChecks.check(user);
            // 密码验证
            this.additionalAuthenticationChecks(user,         
                               (UsernamePasswordAuthenticationToken)authentication)->{
                // 得到输入的密码
                String presentedPassword = authentication.getCredentials().toString();
                // 利用passwordEncoder验证
                if (!this.passwordEncoder.matches(presentedPassword,             
                          userDetails.getPassword())) {
                 throw new BadCredentialsException());
                }
            };   
         }catch(AuthenticationException var7){
            if (!cacheWasUsed) {
                throw var7;
            }
            cacheWasUsed = false;
            // 出现异常时,检索数据库
            user = this.retrieveUser(username, 
                              (UsernamePasswordAuthenticationToken)authentication);
            this.preAuthenticationChecks.check(user);
            this.additionalAuthenticationChecks(user, 
                          (UsernamePasswordAuthenticationToken)authentication);
         }
         
         // 密码是否过期
         this.postAuthenticationChecks.check(user);
         if (!cacheWasUsed) {
            // 从内存或数据库中查出来,放入缓存
            this.userCache.putUserInCache(user);
         }

         Object principalToReturn = user;
         if (this.forcePrincipalAsString) {
            principalToReturn = user.getUsername();
         }

         this.createSuccessAuthentication(principalToReturn, authentication, user);
     };
};

UserDetailsService 组件

//根据用户名返回用户详情
public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
public class User implements UserDetails {
  
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;
    
    getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
}

//基于内存实现UserDetailsService
public class InMemoryUserDetailsManager{
    //储存用户数据的map
    private final Map<String, MutableUserDetails> users = new HashMap();
    //是一个认证管理器
    private AuthenticationManager authenticationManager;

    //创建用户,删除用户,更新用户等操作
    //重要实现UserDetails loadUserByUsername(String var1)方法
}

身份认证管理器

//认证管理器的认证接口
public interface AuthenticationManager {
    Authentication authenticate(Authentication var1) throws AuthenticationException;
}
//实现AuthenticationManager接口,提供认证功能
public class ProviderManager{
    
    private List<AuthenticationProvider> providers;

    //parent值为ProviderManager实例,其属性parent为null, 
    //providers[0]=DaoAuthenticationProvider实例
    private AuthenticationManager parent;
    
    public Authentication authenticate(Authentication authentication){
        //1.先循环providers数组,调用authenticate(authentication)
        Iterator var6 = this.getProviders().iterator();
        while(var6.hasNext()) {
            AuthenticationProvider provider = (AuthenticationProvider)var6.next();
            result = provider.authenticate(authentication);
        }
        //2.调用authenticate(authentication);
        this.parent.authenticate(authentication);
    }
}

AuthenticationProvider

//身份认证提供接口
public interface AuthenticationProvider {
    Authentication authenticate(Authentication var1) throws AuthenticationException;

    boolean supports(Class<?> var1);
}


//一个重要的AuthenticationProvider接口实现
public class DaoAuthenticationProvider{
    private PasswordEncoder passwordEncoder;
    private UserDetailsService userDetailsService;
    //返回认证过的authentication
    Authentication authenticate(Authentication authentication);
}



//认证体接口
public interface Authentication {
    //接口GrantedAuthority.getAuthority()
    // 实现类SimpleGrantedAuthority
    //     属性String role
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials();

    Object getDetails();

    Object getPrincipal();

    boolean isAuthenticated();

    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

//Authentication接口的实现类
public class UsernamePasswordAuthenticationToken{
    private final Object principal;
    private Object credentials;
    private final Collection<GrantedAuthority> authorities;
    private Object details;
    private boolean authenticated = false;
}

        SpringSecurity中使用方式1,在配置类导入UserDetailService/PasswordEncoder的bean。2,继承WebSecurityConfigurerAdapter,configure(AuthenticationManagerBuilder auth)方法auth.userDetailsService().passwordEncoder()。

        自定义身份验证逻辑。1,实现AuthenticationProvider接口2,在configure(AuthenticationManagerBuilder auth)中添加AuthenticationProvider接口的bean。

 MVC匹配器

 mvcMatchers(HttpMethod method,String... patterns) 允许指定要应用限制的HTTP方法和路径

 mvcMatchers(String... patterns) 如果需要应用基于路径的授权限制

 /a 仅匹配路径/a,对于mvc,/a与/a/是一样的,对于ant匹配器,则不同。

/a/* 操作符*会替换一个路径名。匹配/a/b或/a/c,而不是/a/b/c

/a/** 操作符**会替换多个路径名。匹配/a或/a/b或/a/b/c

/a/{param} 适用于具有给定路径参数的路径/a。

/a/{param:regex} 只有当参数的值与给定正则表达式匹配时,才应用于适用于具有给定路径参数的路径/a    mvcMatchers("/a/{param:^[0-9]*$}")

权限配置方法

hasAuthority/Role(String authority) 只有拥有该权限的用户才能调用端点

hasAnyAuthority/Role(String... authorities) 用户必须拥有至少一个指定的权限才能调用端点

access() 基于SpEL构建授权规则。access("hasAuthority('read') and !hasAuthority('delete')")

1.全局方法安全性:预授权和后授权

在项目中开启@EnableGlobalMethodSecurity(prePostEnabled=true)

预授权 满足条件后,才可以调用方法。

 @PreAuthorize("#name == authentication.principal.username") #name 应用方法参数的值

后授权 允许对方法调用,对方法的结果进行验证,如果不满足条件则返回认证失败。

 @PostAuthorize("returnObject.roles.contains('reader')")

 @PostAuthorize("hasPermission(returnObject,'Role_admin')") 1. 实现PermissionEvaluator接口,重写hasPermission(Authentication authentication,Object target,Object permission) 2.配置Perm滤)

        预过滤 框架在调用方法之前过滤参数的值。如果参数违背给定授权规则,则不调用方法。只有符合规则的值会作为参数提供给方法调用

        后过滤 框架在调用方法之后过滤返回的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用和引用,Spring Security是一个用于在Spring应用程序中进行身份验证和授权的框架。它可以通过配置来管理用户认证和授权,提供了一套强大的安全性功能。 在Spring中整合Spring Security,通常需要创建一个Maven项目,并在项目中添加相应的依赖。可以使用注解配置加载Spring容器和安全配置,配置认证页面和授权规则。通过配置WebSecurityConfig类和ApplicationConfig类,指定Spring容器和Spring Security的配置。中的描述,可能是Vue.js中使用的Spring Security自带的登录页面。 在第二版的Spring整合Spring Security中,可能需要导入相关的依赖,如tomcat-embed-jasper等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Spring整合SpringSecurity(一)](https://blog.csdn.net/qq_45297578/article/details/118998861)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [spring整合springSecurity](https://blog.csdn.net/qq_37023928/article/details/105926488)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值