Spring Security 入门(三)

在说完了Spring Security框架的功能和执行流程后,就到了写它Spring Boot的集成,先来看最简的配置:

<parent>
  <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>

   此时,直接运行程序,页面会跳出都会弹出一个“需要授权”的验证框:

  Spring Security会默认生成一个用户,用户名为user,密码可以再IDE的控制台(Console)中查看,输入用户名和密码后就可以访问请求的URL了。

  在我们实际的开发中,肯定需要我们定义访问哪些URL需要登录并拥有相关的权限,访问哪些URL不需要登录,以及验证失败后的跳转页面等。配置方式:定义继承WebSecurityConfigurerAdapter 类WebSecurityConfig类,并加上@Configuration 和@EnableWebSecurity两个注解,开启Spring Security功能,并交由容器进行管理。具体配置如下:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter{

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO Auto-generated method stub
        
        http
            .authorizeRequests()
          //指定任何用户都可以访问'/'路径,permitAll()表示任何用户都可以访问 .antMatchers("/").permitAll()
          //以"/admin/"开头的URL,只能让"USER"角色权限的用户访问 .antMatchers("/user/**").hasRole("USER") .and()
          //通过formLogin方法登录,并设置登录url为/login,登录成功后跳转/user页面 .formLogin() .loginPage("/login").defaultSuccessUrl("/user") .and() .logout()
          //指定登出的url,登出成功后跳转的url路径 .logoutUrl("/logout").logoutSuccessUrl("/login"); }
  
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    /**
     * 在内存中创建一个名为 "user" 的用户,密码为 "pwd",拥有 "USER" 权限,密码使用BCryptPasswordEncoder加密
     */
    auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
      .withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER");
  }

  /**
  * 添加 UserDetailsService, 实现自定义登录校验
  */
  @Override
  protected void configure(AuthenticationManagerBuilder builder) throws Exception{
    builder.userDetailsService(anyUserDetailsService);
  }

}

这是Spring Security进行权限校验管理的一个基本配置啦!

  其实看到这里心里还是有很多疑惑:

  • 一般用户的信息都是保存在数据库中,那就还要涉及从数据库中读取信息进行验证;
  • 登录成功后登录信息保存在哪,保存在SecurityContextHolder中吗?;
  • 一般来说还需要自定义用户认证的流程,因为你要从数据库中读取数据,发生错误时需要相关的提示信息;
  • 用户登录具体的验证流程是什么样的,该如何重写自定义验证流程?;

  这些疑惑网上早就有人想到了,接着看下面的文章。

  在知乎和简书上面找到了两张认证流程的图,直观地展示一下:

  

                           图一

                            图二

  在Spring Security入门(二)中提到,用户登录时登录信息会封装在Authentication中,然后传递给AuthenticationManager实例进行验证。Authentication是如何在AuthenticationManager实例中进行验证的?具体的流程就如图一所示:

  1. AuthenticationManager,它是验证管理类的总接口;
  2. 而具体的验证管理需要ProviderManager类,它具有一个List<AuthenticationProvider> providers属性,这实际上是一个AuthenticationProvider实例构成的验证链;
  3. 链上都是各种AuthenticationProvider实例,这些实例进行具体的验证工作。

  所以根据图二,大致的验证流程如下:

  1. 后端从前端的表单得到用户密码,包装成一个Authentication类的对象;
  2. 将Authentication对象传给“验证管理器”ProviderManager进行验证;
  3. ProviderManager在一条链上依次调用AuthenticationProvider进行验证;
  4. 其中DaoAuthenticationProvider依赖于UserDetailsService,调用UserDetailsService实例的loadUserByUsername方法,会返回一个UserDetails实例,将UserDetails实例与Authentication对象进行比对;
  5. 验证成功则返回一个封装了权限信息的Authentication对象(即对象的Collection<? extends GrantedAuthority>属性被赋值);
  6. 将此对象放入安全上下文SecurityContext中;
  7. 需要时,可以将Authentication对象从SecurityContextHolder上下文中取出。

  还记得上文提到说,用户数据存放于数据库时该如何读取用户信息进行认证吗?没错,就是自定义一个UserDetailsService的类,主要是重写它的loadUserByUsername方法,将用户信息封装在UserDetails对象中。看个具体的栗子:

@Service
public class AnyUserDetailService implements UserDetailsService{
    //DAO层的代码省略
    @Autowired
    private UserMapper userMapper; 
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // TODO Auto-generated method stub
        User user = userMapper.selectByUsername(username); if(user == null) { throw new UsernameNotFoundException("用户不存在!"); } List<SimpleGrantedAuthority> simpleGrantedAuthorities = createAuthorities(user.getRoles()); return new User(userEntity.getUsername(), userEntity.getPassword(), simpleGrantedAuthorities); }

自定义了类之后还要配置才能生效,还是在WebSecurityConfig类中进行配置,参见上文。

猜你喜欢

转载自www.cnblogs.com/linyukun/p/9862730.html