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非常好的一點在於它不需要類型轉換,同時能夠將安全相關的代碼僅僅侷限於註解本身。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章