今日目標,設置 web security。
我們需要對網站做一些權限管理,包含登入等行為,就需要先設置 web security config,以下先做簡易登入設置。
<!-- web security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
package com.example.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
WebSecurityConfigurerAdapter
:提供基本配置,雖然已經被棄用,但小弟才疏學淺,不管怎麼改寫都會報錯... 就只好先繼續用了...@Configuration
:聲明此類別是配置設定@EnableWebSecurity
:啟用 Security 所需的各項配置passwordEncoder()
:自定義如何加密(雜湊)密碼,避免密碼以明文儲存configure(HttpSecurity http)
:自定義安全性設置,例如:某些網頁需要更高權限才可瀏覽等
http.csrf().disable()
:取消 CSRF 設置,避免表單無法正常發送,以安全性考量而言,此設置不應該被禁用,但小弟無論如何都無法在頁面添加 CSRF Token 所以只好先禁用它了package com.example.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
.authorizeRequests()
:做權限分配.antMatchers("/register").permitAll()
:允許所有路徑是 /register
的請求,意思是所有人都可以瀏覽 register 的頁面.anyRequest().authenticated()
:對所有請求要求驗證,意思是要登入才能瀏覽其他頁面.and().formLogin()
:未通過驗證的請求都轉到 login 頁面,如果不設定這個,會直接在頁面上出現 403package com.example.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder().encode("123"))
.roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
.inMemoryAuthentication()
:自定義合法的驗證.withUser("admin")
:使用者帳號設定為 admin.password(passwordEncoder().encode("123"))
:密碼設定為 123,注意要先雜湊過,不然會無法登入.roles("ADMIN")
:設定角色來做權限管理,如果有多個角色也可以一起設定讀者們應該會有個疑問,該不會要手動打入特定帳號密碼才能登入吧,那註冊是寫辛酸的喔?
且慢,當然不是!小弟將在明天的文章實現自定義登入功能!
既然都設定了 password encoder,那就順便處理一下資料庫的密碼是明文這件事情吧~
package com.example.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.util.Set;
@Service
@Validated
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private Validator validator;
@Autowired
private PasswordEncoder passwordEncoder;
public UserModel findUserByEmail(String email) {
return userRepository.findByEmail(email);
}
public UserModel findUserByUsername(String username) {
return userRepository.findByUsername(username);
}
public Integer addUser(UserModel user) {
Set<ConstraintViolation<UserModel>> violations = validator.validate(user);
if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<UserModel> constraintViolation : violations) {
sb.append(constraintViolation.getMessage());
}
throw new ConstraintViolationException(sb.toString(), violations);
}
user.setPassword(passwordEncoder.encode(user.getPassword()));
UserModel newUser = userRepository.save(user);
return newUser.getId();
}
}
你好, 到了spring security 6.0 那兩個configure 已經變成這樣了.
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("admin")
.password(passwordEncoder().encode("123"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
/* anymatchers() deprecated and removed from spring security 6.0, use requestMatchers instead */
http.cors().and().csrf().disable()
.authorizeRequests().requestMatchers("/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
// http....;
return http.build();
}