iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
自我挑戰組

Spring In Action系列 第 9

Spring Security

  • 分享至 

  • xImage
  •  

只要我們引入spring-boot-starter-security:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

就會獲得一個免費的login page,username為user,password會在Spring啟動的時候從console得知。
login form
不過我們並不一定想要一進入webapp就遇到登入頁面,這時候就得需要自己設定其他security相關的東西了。

Spring也提供了password encoder的工具,這個機制可以很方便讓user輸入的密碼經過特定演算法加密,而我們資料庫所儲存的都是加密過後的密碼,而user要登入時SpringApp所進行的驗證也都透過加密過的字串來比對。

package tacos.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig {
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
}
public class SomeClassEnvolveUser{
    @Autowired
    public PasswordEncoder pwdEncoder;
    
    public User createUser(String username, String password){
        User user = new User(username, encoder.encode(password),
                    Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
        return user;
    }
}

身為Java developer,要儲存User資訊,首先肯定會想到來建立了User類別。User類別可以implements了Spring Security的UserDetails,會預先定義了isAccountNonExpired, isEnabled()等等的方法介面,提醒開發可以實作這些方法,方便日後的security相關使用。

再來就是UserDetailService, UserRepository的建立,以及建立一個Thymeleaf的page,這邊也可建立一個類似dto概念的RegistrationForm,並實作一個toUser的方法轉換為User。

有了客製化的user註冊登入機制,再來就是控制哪些地方需要登入,或者哪些功能需要哪些權限。我們可以透過SecurityFilterChain(HttpSecurity http)的Bean簡單做到這件事情:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  return http
    .authorizeRequests()
      .antMatchers("/design", "/orders").hasRole("USER")
      .antMatchers("/", "/**").permitAll()
 
    .and()
    .build();
}

可以看到我們針對不同的路徑決定所需要的權限,或者permitAll,不須權限皆可訪問。而以上可透過access()方法搭配SpEL來有更彈性的寫法:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  return http
    .authorizeRequests()
      .antMatchers("/design", "/orders").access("hasRole('USER')")
      .antMatchers("/", "/**").access("permitAll()")
 
    .and()
    .build();
}

除了自行定義的帳號密碼登入,也可透過OAuth2來達到同樣的目的。引入spring-boot-starter-oauth2-client即可,並在application.properties進行設定。

spring:
  security:
    oauth2:
      client:
        registration:
          facebook:
            clientId: <facebook client id>
            clientSecret: <facebook client secret>
            scope: email, public_profile

Spring Security預設就會為表單附上CSRF(Cross-site request forgery)的防護,這種防護通常是在表單多加上一個隱藏的CSRF token,這個token只有發出表單的server知道,所以當表單被其他程式過一手後,他們不會知道這個token是多少,或者是如何被辨認,就可知道表單有沒有被過一手汙染。

除了上述的防護外,Spring Security也支援method-level的設定,通常就是透過@PreAuthorize(”hasRole(’ADMIN’)”)的annotation來達成,而要能使用@PreAuthorize,就得在SecurityConfig的@Configuration上貼上@EnableGlobalMethodSecurity:

@Configuration
@EnableGlobalMethodSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  ...
}

最後,我們會希望在例如訂單等紀錄上可以紀上是哪個user的,Spring Security也讓我們可以很方便去取得一個authenticated的user instance來去應用,可透過以下幾種方式:

  • 在方法簽章上多加Principal引數
@PostMapping
public String processOrder(@Valid TacoOrder order, Errors errors,
    SessionStatus sessionStatus,
    Principal principal) {
 
  ...
 
  User user = userRepository.findByUsername(
          principal.getName());
 
  order.setUser(user);
 
  ...
 
}
  • 透過Authentication
@PostMapping
public String processOrder(@Valid TacoOrder order, Errors errors,
    SessionStatus sessionStatus,
    Authentication authentication) {
 
  ...
 
  User user = (User) authentication.getPrincipal();
  order.setUser(user);
 
  ...
 
}
  • 貼上@AuthenticationPrincipal在user物件參數上
@PostMapping
public String processOrder(@Valid TacoOrder order, Errors errors, 
    SessionStatus sessionStatus, 
    @AuthenticationPrincipal User user) {
  
  if (errors.hasErrors()) {
    return "orderForm";
  }
  
  order.setUser(user);
  
  orderRepo.save(order);
  sessionStatus.setComplete();
  
  return "redirect:/";
}

上一篇
No Sql Database
下一篇
Configuration
系列文
Spring In Action30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言