Spring Security的概述加原理(有一个清晰的认识)

目录

 结构

流程说明:

所有请求:

登陆流程:

权限与配置

1.权限

 1.UserDetails

 2.GrantedAuthority

 3.UserDetailsService

 5.Authentication

2.配置

1.登陆

 2.设置权限

3.登出相关配置 

4.鉴权失败 

5.加入自定义的Filter

6.记住我功能 


 结构

​    security会对所有进入系统的请求进行拦截,校验每个请求是否有权限访问他所要的资源,由Filter实现的安全框架

​    在初始化Spring Security时创建SpringSecurityFilterChain的过滤器,其类型为FilterChainProxy,他实现了Filter,外部请求会经过此类。

  • AuthenticationManager(认证管理器)
  • AcessDecisionManager(决策管理器)

流程说明:

所有请求:

​    1.在请求开始时**SecurityContextPersistenceFilter** 从SecurityContextRepository 中获取 SecurityContext,然后把它设置给 SecurityContextHolder。

​    2.在请求完成后将SecurityContextHolder 持有的 SecurityContext 再保存到配置好的 SecurityContextRepository,同时清除 securityContextHolder所持有的 SecurityContext;  

登陆流程:

  1. 但路径为登陆路径时,用户的登陆信息会被UsernamePasswordAuthenticationFilter中的UsernamePasswordAuthenticationToken封装成Authentication
  2. 然后将封装好的Authentication传给AuthenticationManager认证管理器进行认证
  3. 认证失败,到AuthenticationFailureHandler 登录失败处理器处理
  4. 认证成功则AuthenticationManager返回一个Authentication实例(包含的权限信息, 身份信息,细节信息通常要把密码轻触)
  5. SecurityContextHolder.getContext().setAuthentication(…)方法 保存SecurityContextHolder 安全上下文 
  6. AuthenticationManager 实现认证的实现类时ProviderManager,他是一个List链式结构,存放了多种认证方式。AuthenticationProvider实现类为 DaoAuthenticationProvider,它的内部又维护着一UserDetailsService负责UserDetails的获取。最终 AuthenticationProvider将UserDetails填充至Authentication。

普通的登陆:

​    当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层否则到 AccessDeniedHandler 鉴权失败处理器处理。 

登出:

​    当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 **logoutSuccessHandler** 登出成功处理,如果登出失败则由 ExceptionTranslationFilter ;如果不是登出路径则直接进入下一个过滤器。

未登录状态:

​    有些uri被人直接输入网址时,而又没有身份,可以继承**AuthenticationEntryPoint**自定义未登录结果返回

异常状态:

ExceptionTranslationFilter: 能够捕获来自 FilterChain 所有的异常,并进行处理。但是它只会处理两类异常: AuthenticationException AccessDeniedException,其它的异常它会继续抛出。 

权限与配置

1.权限

 1.UserDetails

Security 中的用户接口,其存储的就是用户信息 ,我们自定义用户类要实现该接口。

public interface UserDetails extends Serializable {

    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

方法含义如下:

  • getAuthorites:获取用户权限,本质上是用户的角色信息。
  • getPassword: 获取密码。
  • getUserName: 获取用户名。
  • isAccountNonExpired: 账户是否过期。
  • isAccountNonLocked: 账户是否被锁定。
  • isCredentialsNonExpired: 密码是否过期。
  • isEnabled: 账户是否可用。

 2.GrantedAuthority

Security 中的用户权限接口,自定义权限需要实现该接口:

public class MyGrantedAuthority implements GrantedAuthority {    
    private String authority;    
}
  •  authority 表示权限字段

需要注意的是在 config 中配置的权限会被加上 ROLE_ 前缀

​    比如我们的authorizeRequests().antMatchers("/test").hasRole("test"),配置了一个 test 权限但我们存储的权限字段 (authority)应该是 ROLE_test 。

 3.UserDetailsService

Security 中的用户 Service,自定义用户服务类需要实现该接口:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        return ....;
    }
}

loadUserByUsername的作用在上文中已经说明,就是根据用户名查询用户对象 

 4.SecurityContextHolder

        用户在完成登录后 Security 会将用户信息存储到这个类中,之后其他流程需要得到用户信息时都是从这个类中获得,用户信息被封装成 SecurityContext ,而实际存储的类是 SecurityContextHolderStrategy ,默认的SecurityContextHolderStrategy 实现类是 ThreadLocalSecurityContextHolderStrategy 它使用了ThreadLocal来存储了用户信息。

SecurityContextHolder.getContext().setAuthentication(…)

​    对于使用 token 鉴权的系统,我们就可以验证token后手动填充SecurityContextHolder,填充时机只要在执行投票器之前即可,或者干脆可以在投票器中填充,然后在登出操作中清空SecurityContextHolder。 

 5.Authentication

可以理解为authentication就是一组用户名密码信息 ,他也是一个接口

public interface Authentication extends Principal, Serializable {
 
    Collection<? extends GrantedAuthority> getAuthorities();
    Object getCredentials();
    Object getDetails();
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

接口有4个get方法,分别获取

  • Authorities:填充的是用户角色信息。
  • Credentials:填充的是密码。
  • Details:用户信息。
  • Principal: 其填充的是用户名。

因此可以推断其实现类有这4个属性。这几个方法作用如下:

  • getAuthorities: 获取用户权限,一般情况下获取到的是用户的角色信息。
  • getCredentials: 获取证明用户认证的信息,通常情况下获取到的是密码等信息。
  • getDetails: 获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)
  • getPrincipal: 获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserDetails (UserDetails也是一个接口,里边的方法有getUsername,getPassword等)。
  • isAuthenticated: 获取当前 Authentication 是否已认证。
  • setAuthenticated: 设置当前 Authentication 是否已认证(true or false)。

2.配置

在 WebSecurityConfigurerAdapter 这个类里面可以完成上述流程图的所有配置 

@Configuration    
@EnableWebSecurity    
public class SecurityConfig extends WebSecurityConfigurerAdapter {    
    @Override    
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {    
        auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());    
    }    
    @Override    
    public void configure(WebSecurity web) throws Exception {    
        web.ignoring().antMatchers("/resources/**/*.html", "/resources/**/*.js");    
    }    
    @Override    
    protected void configure(HttpSecurity http) throws Exception {    
       http    
       .formLogin()    
       .loginPage("/login_page")    
       .passwordParameter("username")    
       .passwordParameter("password")    
       .loginProcessingUrl("/sign_in")    
       .permitAll()    
       .and().authorizeRequests().antMatchers("/test").hasRole("test")    
       .anyRequest().authenticated().accessDecisionManager(accessDecisionManager())    
       .and().logout().logoutSuccessHandler(new MyLogoutSuccessHandler())    
       .and().csrf().disable();    
       http.addFilterAt(getAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);    
       http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());    
       http.addFilterAfter(new MyFittler(), LogoutFilter.class);    
    }    
}

1. configure(AuthenticationManagerBuilder auth)

   ​   1.AuthenticationManager 的建造器,配置 AuthenticationManagerBuilder 会让Security 自动构建一个 AuthenticationManager

   ​   2.如果想要使用该功能你需要配置一个 UserDetailService 和 PasswordEncoder。

   ​   3.UserDetailsService 用于在认证器中根据用户传过来的用户名查找一个用户,PasswordEncoder 用于密码的加密与比对,我们存储用户密码的时候PasswordEncoder.encode() 加密存储,在认证器里会调用 PasswordEncoder.matches() 方法进行密码比对。如果重写了该方法,Security 会启用 DaoAuthenticationProvider 这个认证器,该认证就是先调用 UserDetailsService.loadUserByUsername 然后使用 PasswordEncoder.matches() 进行密码比对,如果认证成功成功则返回一个 Authentication 对象。
   

configure(WebSecurity web)

这个配置方法用于配置静态资源的处理方式,可使用 Ant 匹配规则。

configure(HttpSecurity http)

http    
.formLogin()    
.loginPage("/login_page")//登录页请求路径
.usernameParameter("username")//用户名属性名    
.passwordParameter("password")//密码属性名    
.loginProcessingUrl("/sign_in")//登录请求路径    
.permitAll()//任意用户可访问。

1.登陆

 //成功登陆进入的页面,必须时post
                .successForwardUrl("/toMain")
                //不能与successForwardUrl共存
                //.successHandler(new MyAuthenticationSucessHandler("/main.html"))
                .failureForwardUrl("/toError")
                //不能与failureForwardUrl一起用
                //.failureHandler(new ForwardAuthenticationFAilureHandler("http://app.dodoge.me/"))

 2.设置权限

http    
.authorizeRequests()    
.antMatchers("/test").hasRole("test")//设定了访问/test要有test权限    
.anyRequest()//表示所有请求
.authenticated()//表示已登录用户才能访问    
.accessDecisionManager(accessDecisionManager());//表示绑定在 url 上的鉴权管理器
http.authorizeRequests()
 //.regexMatchers(HttpMethod.GET,"/demo").permitAll()//限制只有get请求才能进入/demo
.antMatchers("/main1.html").access("hasRole('abc')")//角色赋予
 //.antMatchers("/main1.html").hasIpAddress("127.0.0.1")//限制ip地址进入

3.登出相关配置 

http    
.logout()    
.logoutUrl("/logout")    
.logoutSuccessHandler(new MyLogoutSuccessHandler())

4.鉴权失败 

http    
.exceptionHandling()    
.accessDeniedHandler(new MyAccessDeniedHandler());

5.加入自定义的Filter

http.addFilterAfter(new MyFittler(), LogoutFilter.class);    
http.addFilterAt(getAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
  • addFilterBefore 加在对应的过滤器之前,
  • addFilterAfter 加在对应的过滤器之后,
  • addFilterAt 加在过滤器同一位置 

注:调用 addFilterAt 方法插入的 Filter ,会在这个位置上的原有 Filter 之前执行 

6.记住我功能
 

http.rememberMe()
                //定义生效时间,单位秒
                .tokenValiditySeconds(60)
                //修改remem-me的固定名字
                //.rememberMeParameter()
                //自定义逻辑
        .userDetailsService(userDetailsService)
                .tokenRepository(persistentTokenRepository);//持久化操作让token

定义构造AccessDecisionManager的方法并在配置类中调用,配置参考 configure(HttpSecurity http) 说明:

public AccessDecisionManager accessDecisionManager(){    
    List<AccessDecisionVoter<? extends Object>> decisionVoters    
            = Arrays.asList(    
            new MyExpressionVoter(),    
            new WebExpressionVoter(),    
            new RoleVoter(),    
            new AuthenticatedVoter());    
    return new UnanimousBased(decisionVoters);
}

投票管理器会收集投票器投票结果做统计,最终结果大于等于0代表通过;每个投票器会返回三个结果:-1(反对),0(通过),1(赞成)。

借鉴:

工作原理:(48条消息) Spring Security的工作原理_-------江湖-------的博客-CSDN博客_springsecurity原理

参考:Spring Security 入门原理及实战 - 逃离沙漠 - 博客园 (cnblogs.com)

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值