SPRING实战(1)、Spring基础

Spring的核心是提供了一个 容器(container),通常称为Spring应用上下文(Spring application context),它们会创建和管理应用组件。将bean装配在一起的行为是通过一种基于 依赖注入(dependency injection,DI) 的模式实现的。Spring通过配置文件或者基于java配置将bean装配在一起。

@Configuration注解会告知Spring这是一个配置类,会为Spring应用上下文提供bean。这个配置类的方法使用@Bean注解进行了标注,表明这些方法所返回的对象会以bean的形式添加到Spring的应用上下文中(默认情况下,这些bean所对应的bean ID与定义它们的方法名称是相同的)。但是需要注意的是无论使用Java还是使用xml的显式配置,只有当Spring不能进行自动配置的时候才是必要的。

自动配置起源于所谓的 自动装配(autowiring) 和 组件扫描(component scanning)。借助组件扫描技术,Spring能够自动发现应用类路径下的组件,并将它们创建成Spring应用上下文中的bean。借助自动装配技术,Spring能够自动为组件注入它们所依赖的其他bean。

Spring Boot starter依赖的特别之处在于它们本身并不包含库代码,而是传递性拉去其他的库。这种starter依赖主要有3个好处:

  1. 构建文件会显著减小并且更易于管理,因为这样不必为每个所需的依赖库都声明依赖。

  2. 我们能够根据它们所提供的功能来思考依赖,而不是根据库的名称。如果是开发Web应用,那么你只需要添加web starter就可以了,而不必添加一堆单独的库再编写Web应用。

  3. 我们不必再担心库版本的问题。你可以直接相信给定版本的Spring Boot,传递性引入的库的版本是兼容的。现在,你只需要关心使用的是哪个版本的Spring Boot就可以。

构建规范还包含一个Spring Boot插件。这个插件提供了一些重要的功能:

  1. 它提供了一个Maven goal,允许我们使用Maven来运行应用。
  2. 它会确保依赖的所有库都会包含在可执行JAR文件中,并且能够保证它们在运行时类路径下是可用的。
  3. 它会在JAR中生成一个manifest文件,将引导类声明为可执行JAR的主类。

@SpringBootApplication是一个组合注解,它组合了3个其他的注解。

  • @SpringBootConfiguration:将该类声明为配置类。这个注解实际上是@Configuration注解的特殊形式。
  • @EnableAutoConfiguration:启用Spring Boot的自动配置。
  • @ComponentScan:启用组件扫描。这样我们能够通过像@Component、@Controller、@Service这样的注解声明其他类,Spring会自动发现它们并将它们注册为Spring应用上下文中的组件。

启动类的main()方法:

public class BootAdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootAdminServerApplication.class, args);
    }
}

这个main()方法会调用SpringApplication中静态的run()方法,后者会真正执行应用的引导过程,也就是创建Spring的应用上下文。在传递给run()的两个参数中,一个是配置类,另一个是命令行参数。

SpringBootTest:

@RunWith(SpringRunner.class)
@SpringBootTest

@WebMvcTest(MvcController.class)
public class BootAdminServerApplicationTests {

    @Test
    public void contextLoads() {
    }

}

  • @RunWith(SpringRunner.class)注解:运行容器Spring,@RunWith是JUnit的注解,它会提供一个测试运行器(runner)来指导JUnit如何运行测试。SpringRunner是SpringJUnit4ClassRunner的别名。
  • @SpringBootTest会告诉JUnit在启动测试的时候要添加上Spring Boot的功能。
  • @WebMvcTest注解。这是Spring Boot所提供的一个特殊测试注解,它会让这个测试在Spring MVC应用的上下文中执行。除了使用该注解外,还需要注入MockMvc,Spring自带了一个强大的Web框架,名为Spring MVC。Spring MVC的核心是 控制器(controller) 的理念。控制器是处理请求并以某种方式进行信息响应的类。

Spring Boot DevTools提供了很多开发工具,包括:

  • 代码变更后应用的自动重启;
  • 当面向浏览器的资源等发生变化时,会自动刷新浏览器;
  • 自动禁用模板缓存;
  • 如果使用H2数据库的话,内置了H2控制台。

当探测到变更的时候,DevTools只会重新加载包含项目代码的类加载器,并重启Spring的应用上下文,在这个过程中另外一个类加载器和JVM会原封不动。这个策略非常精细,但是它能减少应用启动的时间。 这种策略的一个不足之处就是自动重启无法反映依赖项的变化。这是因为包含依赖库的类加载器不会自动重新加载。这意味着每当我们在构建规范中添加、变更或移除依赖的时候,为了让变更生效,我们需要重新启动应用。

默认情况下,像Thymeleaf和FreeMarker这样的模板方案在配置时会缓存模板解析的结果。

DevTools在运行的时候,它会和你的应用程序一起,同时自动启动一个LiveReload服务器。LiveReload服务器本身并没有太大的用处。但是,当它与LiveReload浏览器插件结合起来的时候,就能够在模板、图片、样式表、JavaScript等(实际上,几乎涵盖为浏览器提供服务的所有内容)发生变化的时候自动刷新浏览器。LiveReload有针对Google Chrome、Safari和Firefox的浏览器插件。

使用Lombok库消除样板代码。如:@Data注解就是由Lombok提供的,它会告诉Lombok生成所有缺失的方法,同时还会生成所有以final属性作为参数的构造器。@Slf4j在运行时,它会在这个类中自动生成一个SLF4J(Simple Logging Facade for Java)Logger。@NoArgsConstructor: 注解在类,生成无参的构造方法;除了类级别注解还有方法或字段注解。详情看:官方文档

Spring支持Java的Bean校验API(Bean Validation API,也被称为JSR-303)。借助Spring Boot,要在项目中添加校验库,我们甚至不需要做任何特殊的操作,这是因为Validation API以及Validation API 的Hibernate实现将会作为Spring Boot web starter的传递性依赖自动添加到项目中。

Spring Data为所有项目提供了一项最有趣且最有用的特性,就是基于repository规范接口自动生成repository的功能。为了将Ingredient声明为JPA实体,它必须添加@Entity注解。它的id属性需要使用@Id注解,以便于将其指定为数据库中唯一标识该实体的属性,可以通过@GeneratedValue设置id增长策略。JPA需要实体有一个无参构造器。Spring Data定义了一组小型的领域特定语言(Domain-Specific Language,DSL)。

保护Spring应用的第一步就是将Spring Boot security starter依赖添加到构建文件中。通过将security starter添加到项目的构建文件中,我们得到了如下的安全特性:

  • 所有的HTTP请求路径都需要认证; 
  • 不需要特定的角色和权限;
  • 没有登录页面; 
  • 认证过程是通过HTTP basic认证对话框实现的; 
  • 系统只有一个用户,用户名为user。

Spring Security为配置用户存储提供了多个可选方案,包括: 基于内存的用户存储; 基于JDBC的用户存储; 以LDAP作为后端的用户存储; 自定义用户详情服务。无论采取何种用户存储方式,都需要继承WebSecurityConfigurerAdapter类,并重写 configure()方法进行配置。而且子类需要使用注解@Configuration、@EnableWebSecurity启用配置。

1、基于内存的用户存储

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.inMemoryAuthentication()
      .withUser("buzz")
        .password("infinity")
        .authorities("ROLE_USER")
      .and()
      .withUser("woody")
        .password("bullseye")
        .authorities("ROLE_USER"); 
}

2、基于JDBC的用户存储

@Autowired
DataSource dataSource;
//如果数据库表设置与UserDetailsService默认实现中的结构一致

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.jdbcAuthentication()
      .dataSource(dataSource);
}
//如果数据库表设置与UserDetailsService默认实现中的结构不一致

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth
    .jdbcAuthentication()
      .dataSource(dataSource)
      .usersByUsernameQuery(
          "select username, password, enabled from Users " +
          "where username=?")
      .authoritiesByUsernameQuery(
          "select username, authority from UserAuthorities " +
          "where username=?");
}

//密码加密情况

@Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {  
    auth
      .jdbcAuthentication()
        .dataSource(dataSource)
        .usersByUsernameQuery(
            "select username, password, enabled from Users " +
            "where username=?")
        .authoritiesByUsernameQuery(
            "select username, authority from UserAuthorities " +
            "where username=?")
        .passwordEncoder(new StandardPasswordEncoder("53cr3t"); 
  }

【注意】

  • 将默认的SQL查询替换为自定义的设计时,很重要的一点就是要遵循查询的基本协议。所有查询都将用户名作为唯一的参数。认证查询会选取用户名、密码以及启用状态信息。权限查询会选取零行或多行包含该用户名及其权限信息的数据。群组权限查询会选取零行或多行数据,每行数据中都会包含群组ID、群组名称以及权限。
  • passwordEncoder()方法指定一个密码转码器(encoder),passwordEncoder()方法可以接受Spring Security中PasswordEncoder接口的任意实现。Spring Security的加密模块包括了多个这样的实现,当然也可自定义实现。
    • BCryptPasswordEncoder:使用bcrypt强哈希加密。
    • NoOpPasswordEncoder:不进行任何转码。
    • Pbkdf2PasswordEncoder:使用PBKDF2加密。
    • SCryptPasswordEncoder:使用scrypt哈希加密。
    • StandardPasswordEncoder:使用SHA-256哈希加密。

3、以LDAP(Lightweight Directory Access Protocol,轻量级目录访问协议)作为后端的用户存储

@Override
protected void configure(AuthenticationManagerBuilder auth)
    throws Exception {
  auth
    .ldapAuthentication()
      .userSearchFilter("(uid={0})")
      .groupSearchFilter("member={0}");
}

【注意】基于LDAP认证的默认策略是进行绑定操作,直接通过LDAP服务器认证用户。另一种可选的方式是进行比对操作。这涉及将输入的密码发送到LDAP目录上,并要求服务器将这个密码和用户的密码进行比对。因为比对是在LDAP服务器内完成的,实际的密码能保持私密。默认情况下,Spring Security的LDAP认证假设LDAP服务器监听本机的33389端口。当LDAP服务器启动时,它会尝试在类路径下寻找LDIF文件来加载数据。LDIF(LDAP Data Interchange Format,LDAP数据交换格式)是以文本文件展现LDAP数据的标准方式。每条记录可以有一行或多行,每项包含一个name:value配对信息。记录之间通过空行进行分割。

4、自定义用户详情服务

1、//通过实现UserDetails接口,能够提供更多信息给框架,比如用户都被授予了哪些权限以及用户的账号是否可用
public class User implements UserDetails {
}

2、@Repository
public interface UserRepository extends CrudRepository<User, Long> {
  User findByUsername(String username);
  
}

3、@Service
public class UserRepositoryUserDetailsService implements UserDetailsService {

  private UserRepository userRepo;

  @Autowired
  public UserRepositoryUserDetailsService(UserRepository userRepo) {
    this.userRepo = userRepo;
  }
  
  @Override
  public UserDetails loadUserByUsername(String username)
      throws UsernameNotFoundException {
    User user = userRepo.findByUsername(username);
    if (user != null) {
      return user;
    }
    throw new UsernameNotFoundException("User '" + username + "' not found");
  }
}

 

4、重写configure()

  @Bean
  public PasswordEncoder encoder() {
    return new StandardPasswordEncoder("53cr3t");
  }
  
  @Override
  protected void configure(AuthenticationManagerBuilder auth)
      throws Exception {

    auth
      .userDetailsService(userDetailsService)
      .passwordEncoder(encoder());
    
  }

可以使用HttpSecurity配置的功能包括:

  • 在为某个请求提供服务之前,需要预先满足特定的条件;
  • 配置自定义的登录页;
  • 支持用户退出应用;
  • 预防跨站请求伪造。
    @Override
      protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
            .antMatchers("/design", "/orders")
              .access("hasRole('ROLE_USER')")
            .antMatchers("/", "/**").access("permitAll")
          .and()
            .formLogin()
              .loginPage("/login")
          .and()
            .logout()
              .logoutSuccessUrl("/")
          .and()
            .csrf()
              .ignoringAntMatchers("/h2-console/**")
          .and()  
            .headers()
              .frameOptions()
                .sameOrigin();
      }

跨站请求伪造(Cross-Site Request Forgery,CSRF)是一种常见的安全攻击。它会让用户在一个恶意的Web页面上填写信息,然后自动(通常是秘密的)将表单以攻击受害者的身份提交到另外一个应用上。为了防止这种类型的攻击,应用可以在展现表单的时候生成一个CSRF token,并放到隐藏域中,然后将其临时存储起来,以便后续在服务器上使用。在提交表单的时候,token将和其他的表单数据一起发送至服务器端。请求会被服务器拦截,并与最初生成的token进行对比。如果token匹配,那么请求将会允许处理;否则,表单肯定是由恶意网站渲染的,因为它不知道服务器所生成的token。Spring Security提供了内置的CSRF保护。更幸运的是,默认它就是启用的,我们不需要显式配置,我们唯一需要做的就是确保应用中的每个表单都要有一个名为“_csrf”的字段,它会持有CSRF token。

确定用户是谁常用的方式如下:

  • 注入Principal对象到控制器方法中;
  • 注入Authentication对象到控制器方法中,需要进行类型转换。
  • 使用SecurityContextHolder来获取安全上下文; 
  • 使用@AuthenticationPrincipal注解来标注方法。@AuthenticationPrincipal非常好的一点在于它不需要类型转换,同时能够将安全相关的代码仅仅局限于注解本身。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值