springboot集成springSecurity(总结篇)

 

 

@Override

protected void configure(HttpSecurity http) throws Exception {

        http.cors(c -> {

            CorsConfigurationSource source = request -> {

                CorsConfiguration config = new CorsConfiguration();

                config.setAllowedOrigins(Arrays.asList("*"));

                config.setAllowedMethods(Arrays.asList("*"));

                return config;

            };

            c.configurationSource(source);

        });

        …

}
 

 

 

 

SpringSecurity核心是什么?

过滤器链

在这里插入图片描述

有哪些组件?

AuthenticationManager 认证管理器(所有的认证都是他负责完成)【注意它是一个接口,一般使用实现类是ProviderManager】

认证

SpringSecurity底层认证默认是通过UsernamePasswordAuthenticationFilter进行处理的

什么叫认证?

如何认证?

1、UsernamePasswordAuthenticationFilter
通过这个filter把登陆的用户名和密码拿到,组织一个权限对象(UsernamePasswordAuthenticationToken)只包含用户名和登录时,采集的密码
2、ProviderManager.authenticate(authRequest)调用权限认证器进行验证
2.1权限验证码内部执行authenticate验证方法
   拿到身份认证提供者(AuthenticationProvider集合)通过源码分析,里面只包含一个实例为DaoAuthenticationProvider
   provider.authenticate(authentication)的认证
   DaoAuthenticationProvider.authenticate(authentication)的认证方法
   最终调用了DaoAuthenticationProvider父类的AbstractUserDetailsAuthenticationProvider的authenticate方法
2.2AbstractUserDetailsAuthenticationProvider的authenticate逻辑
   如果你配置了userCache缓存策略,首先通过缓存找,如果找不到在来
   //检索查询user
   user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication)
   这里就拿到了用户信息(数据库或者缓存中拿的)
   this.preAuthenticationChecks.check(user);//验证用户状态【可用、密码过期、锁定、账号过期】
   additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);//进行密码匹配验证
   //验证通过最终又返回UsernamePasswordAuthenticationToken,这些把user的权限信息也放到UsernamePasswordAuthenticationToken里面去了
   //这笔执行完了,就回到了UsernamePasswordAuthenticationFilter的attemptAuthentication(如果没抛异常的话,那就successfulAuthentication调用认证成功方法了)
   //具体调用链路查看UsernamePasswordAuthenticationFilter父类(AbstractAuthenticationProcessingFilter)的doFilter方法
   return createSuccessAuthentication(principalToReturn, authentication, user);
   //最终实现由子类DaoAuthenticationProvider实现
2.3DaoAuthenticationProvider.retrieveUser
    //通过UserDetailsService接口实现类找,所以说为什么必须要去实现这个呢,因为这个是必须的
    UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username)

认证需要哪些环节参与?

(1)UsernamePasswordAuthenticationFilter 认证过滤器

(2)UsernamePasswordAuthenticationToken 携带的登录用户信息

(3)ProviderManager.authenticate(authRequest)调用权限认证器进行验证

(4)DaoAuthenticationProvider.retrieveUser

(5)通过UserDetailsService接口实现类找,所以说为什么必须要去实现这个呢,因为这个是必须的
    UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username)

如何自定义认证逻辑?

自定义认证逻辑很简单了,首先自定义登录过滤器,把登陆时,提供的用户名和密码收集到。然后调用认证器的逻辑进行认证;

授权

授权需要哪些环节参与

(1)BasicAuthenticationFilter默认的基础的验证过滤器

1、从请求header把里面携带的token拿到。然后解析得到具体的用户名称信息;

2、然后根据用户信息通过认证过程存储的用户权限信息拿出来;

3、然后把这些东西交给springSecurity,通过springSecurity给当前这个用户授予对应的权限;

4、实现BasicAuthenticationFilter最基础的权限过滤器接口,然后重写doFilterInternal方法;(方法实现思路,跟1-3步描述思路一致)

下面是自定义的授权过滤器

//根据request获取请求头里面携带的token,然后通过对token进行解析,拿到用户userName,然后在通过username去redis里面查询,如果找到了,把返回用户认证信息

需要在配置类把自定义的授权过滤器加进去

AuthenticationManager 认证管理器

在什么情况下需要使用身份(权限)验证器进行验证?

比如说在通过UsernamePasswordAuthenticationFitler

这个过滤器就是用于拦截登录逻辑的,也就是说你刚要登录,必须先经过我进行拦截;既然能够拦截,那么在当前拦截器里面就能够拿到用户客户端提交的用户号和密码;

在默认的UsernamePasswordAuthenticationFitler里面是通过把用户名和密码拿到,然后生成一个XXXToken,这个token其实就是一个身份信息是Authentication接口的实例;

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password)这个身份信息里面存了用户的名称和密码【记住,下面要考--哈哈】

然后把身份信息交给身份证(认证)管理器进行认证

this.getAuthenticationManager().authenticate(authRequest)【在看下面具体分析认证管理器是如何进行认证的】

然后最终认证成功,返回了UsernamePasswordAuthenticationToken,但是这个和登录时候的不是一个对象并且里面出了有用户名和密码信息,还包含认证成功后,存放的权限信息

this.getAuthenticationManager().authenticate(authRequest)【在看下面具体分析认证管理器是如何进行认证的】

首先脑补,自己想他可能会干什么。

(1)你既然是验证管理器,你肯定是执行的验证过程;

(2)反过来想,我只是把用户名和密码封装成身份信息传给你,然后交给你验证,那你咋验证?如何验证?这个就先看源码?.authenticate(authRequest)验证过程

开始上面说了getAuthenticationManager()拿到的实例就是ProviderManager,那我们就看ProviderManager的authenticate方法

划线的地方关键性分析

【1】在上面已经分析出来了,authentication的实例就是UsernamePasswordAuthenticationToken

//那我就可以理解authentication就是UsernamePasswordAuthenticationToken.class
Class<? extends Authentication> toTest = authentication.getClass()

【2】取到的是什么东西?
getProviders()
//身份验证提供者(你怎么来的)到底是什么实例
List<AuthenticationProvider> providers;
【通过查看构造函数发现,构造这个对象的时候必须提供一个或者多个身份验证提供者】
这个时候,我想看默认提供的是什么,那在哪里初始化提供的呢?【断点走一波】

下面分析完了providers只有一个就是DaoAuthenticationProvider(dao身份认证提供者)

DaoAuthenticationProvider这个里面包含了userDetailsService和passwordEncoder(如果存在时)

DaoAuthenticationProvider父类AbstractUserDetailsAuthenticationProvider
//是否支持,也就是说如果你是用户密码身份token的时候,我能支持;恰好在最开始使用认证管理器调用的验证方法的时候,提供的恰好就是UsernamePasswordAuthenticationToken的实例(可能是其子类--覆盖自定义)
public boolean supports(Class<?> authentication) {
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}

构造方法要求必须提供一个身份验证码

【2.1】providers那个提供的

由配置类触发调用的

继续疑问,配置类里面的authenticationProviders哪里来的哟?【断点在看呗】

猜想,既然是配置类,那么肯定在配置类初始化的时候,进行设置的三。

找到入口

配置类初始化入口在这里?

【3】result = provider.authenticate(authentication)

其实执行的就是DaoAuthenticationProvider的authenticate方法(上面2里面有分析)

通过代码查看,起父类AbstractUserDetailsAuthenticationProvider中的authenticate方法【因为子类没实现--抽象父类默认实现】
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    //authentication就是【UsernamePasswordAuthenticationToken】里面携带的就是用户信息
    String username = determineUsername(authentication);//取出用户名
    boolean cacheWasUsed = true;
    //这里通过缓存查询是否存在,如果存在直接返回UserDetails
    UserDetails user = this.userCache.getUserFromCache(username);
    if (user == null) {
        cacheWasUsed = false;
        try {
            //查询用户信息【也是由子类完成】看下面
            user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
        }
        catch (UsernameNotFoundException ex) {
            this.logger.debug("Failed to find user '" + username + "'");
            if (!this.hideUserNotFoundExceptions) {
                throw ex;
            }
            throw new BadCredentialsException(this.messages
                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
        Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
    }
    try {
        //在身份验证之前先判断用户状态是不是正常的嘛isAccountNonLocked是否被锁定、isEnabled是否可用、isAccountNonExpired账号是否过期、isCredentialsNonExpired密码是否过期
        this.preAuthenticationChecks.check(user);
        //真正执行身份验证,拿到查询出来的user(数据库或者缓存等等),authentication通过登录获取的用户身份信息【UsernamePasswordAuthenticationToken】
        //这里其实就是拿到密码进行比较
        additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
    }
    catch (AuthenticationException ex) {
        if (!cacheWasUsed) {
            throw ex;
        }
        // There was a problem, so try again after checking
        // we're using latest data (i.e. not from the cache)
        cacheWasUsed = false;
        user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
        this.preAuthenticationChecks.check(user);
        additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
    }
    this.postAuthenticationChecks.check(user);
    if (!cacheWasUsed) {
        this.userCache.putUserInCache(user);
    }
    Object principalToReturn = user;
    if (this.forcePrincipalAsString) {
        principalToReturn = user.getUsername();
    }
    return createSuccessAuthentication(principalToReturn, authentication, user);
}

由子类DaoAuthenticationProvider实现的
protected void additionalAuthenticationChecks(UserDetails userDetails,
        UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    //如果证书为空,那就抛异常    
    if (authentication.getCredentials() == null) {
        this.logger.debug("Failed to authenticate since no credentials provided");
        throw new BadCredentialsException(this.messages
                .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
    }
    //获取证书密码信息
    String presentedPassword = authentication.getCredentials().toString();
    //通过初始化设置的密码验证器进行匹配,匹配其实就是把登陆【UsernamePasswordAuthenticationToken存放的密码与查询出来的密码进行比较】如果比匹配就抛异常
    if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
        this.logger.debug("Failed to authenticate since password does not match stored value");
        throw new BadCredentialsException(this.messages
                .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
    }
}

由子类DaoAuthenticationProvider实现的
@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
        throws AuthenticationException {
    prepareTimingAttackProtection();
    try {
        //通过getUserDetailsService的实现类,调用loadUserByUsername方法,最终返回UserDetails
        UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException(
                    "UserDetailsService returned null, which is an interface contract violation");
        }
        return loadedUser;
    }
    catch (UsernameNotFoundException ex) {
        mitigateAgainstTimingAttack(authentication);
        throw ex;
    }
    catch (InternalAuthenticationServiceException ex) {
        throw ex;
    }
    catch (Exception ex) {
        throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
    }
}

//自定义的userDetailsService
@Service//作为一个bean,因为在进行设置DaoAuthenticationProvider的时候,通过spring容器里面拿的。
public class WolfExtendUserDetailService implements UserDetailsService {
    //具体方法里面的实现
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户信息,不管从缓存或者任何地方吧用户信息及用户的权限信息拿出来
        //然后封装为UserDetails给返回回去【上一步的下面逻辑需要拿到UserDetails和UsernamePasswordAuthenticationToken里面的密码信息进行比对】
    }
}

【3.1】UserCache 缓存【有一篇专门的文章分析缓存--看完秒杀一切】

默认实现是NullUserCache

如果说后面想自定义实现userCache直接定义实例,设置到DaoAuthenticationProvider里面覆盖userCache即可;因为只有你实现了,就可以通过username去查询权限信息等等

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Spring Security是一个强大的安全框架,可以在Spring Boot应用程序中使用。Spring Boot可以轻松地集成Spring Security,为应用程序提供认证和授权的功能。 要集成Spring Security,需要完成以下步骤: 1.添加Spring Security依赖项:在pom.xml文件中添加Spring Security依赖项。 2.配置Spring Security:创建一个Security配置类,并在其中定义安全规则。 3.启用Spring Security:在应用程序的启动类中使用@EnableWebSecurity注释启用Spring Security。 通过这些步骤,您可以为Spring Boot应用程序添加安全性,并保护它免受未经授权的访问。 ### 回答2: Spring Boot是一个相当流行的Java框架,它能够极大地简化Java开发者的工作,并且支持大量的集成,其中包括集成Spring SecuritySpring Security是一个强大的框架,用于保护Web应用程序的安全性。在本文中,我们将讨论Spring Boot和Spring Security集成。 一、集成Spring Security依赖 要集成Spring Security,我们需要在我们的Spring Boot应用程序中添加Spring Security的依赖项。我们可以使用 Maven 或 Gradle 来管理我们的项目依赖关系。 在下面的示例中,我们将使用Maven。在pom.xml文件中,添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 二、配置Spring Security 完成依赖注入后,我们需要配置Spring Security,以确保我们的应用程序与身份验证和授权有关的所有部分都按照预期进行工作。 我们可以通过创建一个配置类来完成此操作。 首先,我们需要创建一个@Configuration注释的类,并使用@EnableWebSecurity注释进行注释。 然后,我们可以扩展WebSecurityConfigurerAdapter类,并覆盖configure方法。 ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { // security配置具体实现 @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); } } ``` 在上面的示例中,我们创建了一个名为SecurityConfig的类,并使用@EnableWebSecurity注释进行注释。我们还覆盖了WebSecurityConfigurerAdapter的configure方法,并定义了我们的HTTP安全配置。 在此示例中,我们允许任何请求进行身份验证,并定义了基本身份验证。 三、添加用户和角色 我们已经定义了我们的安全配置,但还没有定义任何用户和角色。我们可以通过使用 @Configuration注释和@ConfigurationProperties注释来添加用户和角色。 我们可以先定义一个自定义属性的Java类,如下: ``` @ConfigurationProperties(prefix = "myapp") public class SecurityProperties { private String username; private String password; private String role; // getter和setter方法 } ``` 然后,在我们的配置类中,我们可以使用 Java Config 的方式将其注入到类中: ``` @Configuration @EnableWebSecurity @EnableConfigurationProperties(SecurityProperties.class) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SecurityProperties securityProperties // security配置具体实现 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser(securityProperties.getUsername()) .password("{noop}" + securityProperties.getPassword()) .roles(securityProperties.getRole()); } } ``` 上面的示例中,我们首先使用@EnableConfigurationProperties注释了SecurityProperties类,并将其自动注入到我们的配置类中。然后,我们将使用该类中的属性来创建内存身份验证,然后配置用户名,密码和角色,这将使用我们在配置文件中定义的账号密码。 四、使用自定义登录页面 Spring Security提供了默认的登录页面,但如果我们希望使用自己的自定义登录页面,则需要添加一个控制器来处理登录请求,并在配置类中进行设置。 ``` @Controller public class LoginController { @GetMapping("/login") public String getLoginPage() { return "login"; } } @Configuration @EnableWebSecurity @EnableConfigurationProperties(SecurityProperties.class) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SecurityProperties securityProperties // security配置具体实现 @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 添加需要放过的静态资源 .antMatchers("/", "/home", "/register").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") // 设置自定义登录页面 .permitAll() .and() .httpBasic() .and() .logout() .permitAll(); } } ``` 上面的示例中,我们创建了一个名为LoginController的控制器,用于处理登录请求。 然后,我们在configure方法中定义了需要允许所有用户访问的页面。 最后,我们配置了自定义登录页面,并将其设置为/login。 总结 在这文章中,我们讨论了如何集成 Spring SecuritySpring Boot 的应用程序中。 我们首先添加了Spring Security的依赖项,并创建了一个配置类。 然后,我们添加了用户和角色,并定义了如何使用自定义登录页面。 通过完成这些步骤,我们可以确保我们的应用程序安全,并保护客户端免受攻击。 ### 回答3: Spring Boot是一种用于轻松构建Java应用程序的框架,而Spring Security是一个用于保护应用程序的身份验证和授权框架。在这里,我们将讨论如何将Spring Boot集成Spring Security。 首先,我们需要在pom.xml文件中添加Spring Security依赖项: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 接下来,我们需要配置Spring Security。这可以通过创建一个类来完成,并使用@Configuration注释来进行注释。我们还需要扩展WebSecurityConfigurerAdapter类。 ``` @EnableWebSecurity @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/", "/home").permitAll() .antMatchers("/admin").hasRole("ADMIN") .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user").password("{noop}password").roles("USER") .and() .withUser("admin").password("{noop}password").roles("USER", "ADMIN"); } } ``` 在此示例中,我们启用了Web Security,并配置了以下内容: 1. 除了主页之外的所有页面都需要身份验证 2. 如果用户拥有ADMIN角色,则允许访问/admin页面 3. 禁用跨站点请求伪造(CSRF)保护 4. 配置表单登录和跳转页面 5. 使用内存身份验证管理器配置了两个用户 现在,我们可以通过以下方式测试Spring Security: 1. 启动应用程序 2. 访问http://localhost:8080/login 3. 输入用户名和密码(user/password 或 admin/password) 4. 尝试访问http://localhost:8080/admin 如果用户拥有ADMIN角色,则允许访问/admin页面,否则将显示403错误页面。 总之,Spring Boot集成Spring Security是一种用于保护应用程序的身份验证和授权框架。我们可以轻松地使用Spring Security通过添加依赖项和配置文件来保护我们的应用程序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值