Xem bài gốc tại đây:

1. Giới thiệu

Bài viết này sẽ tập trung vào Đăng nhập với Spring Security. Chúng tôi sẽ xây dựng dựa trên ví dụ Spring MVC đơn giản trước đó , vì đó là một phần cần thiết của việc thiết lập ứng dụng web cùng với cơ chế đăng nhập.
 

2. Dependencies của Maven

Khi làm việc với Spring Boot, trình khởi động spring-boot-starter-security sẽ tự động bao gồm tất cả các dependency như spring-security-core , spring-security-web và spring-security-config:

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

Trong trường hợp chúng ta không sử dụng Spring Boot, vui lòng xem bài viết Spring Security with Maven mô tả cách thêm tất cả các dependency bắt buộc. Cả hai dependency tiêu chuẩn spring-security-web và spring-security-config sẽ rất cần thiết.

3. Cấu hình Java Spring Security

Hãy bắt đầu bằng cách tạo một class configuration Spring Security extends WebSecurityConfigurerAdapter.

Bằng cách thêm @EnableWebSecurity , chúng ta nhận được hỗ trợ tích hợp Spring Security và MVC:

@Configuration
@EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        // authentication manager (see below)
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // http builder configurations for authorize requests and form login (see below)
    }
}

Trong ví dụ này, chúng ta đã sử dụng authentication in-memory và xác định 3 user.

Tiếp theo, chúng ta đi qua các phần tử chúng ta đã sử dụng để tạo cấu hình form đăng nhập.

Trước tiên, hãy build Authentication Manager.

3.1. Authentication Manager

Authentication Provider được hỗ trợ bởi một implementation đơn giản, in-memory - đặc biệt là InMemoryUserDetailsManager. Điều này rất hữu ích cho việc tạo mẫu nhanh khi chưa cần đến một cơ chế ổn định đầy đủ:

protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user1").password(passwordEncoder().encode("user1Pass")).roles("USER")
        .and()
        .withUser("user2").password(passwordEncoder().encode("user2Pass")).roles("USER")
        .and()
        .withUser("admin").password(passwordEncoder().encode("adminPass")).roles("ADMIN");
}

Ở đây, chúng ta cấu hình 3 user với username, password và role được cố định.

Khởi đầu với Spring 5, chúng ta cũng phải xác định một bộ PasswordEncoder . Trong ví dụ của chúng tôi, chúng tôi đã sử dụng BCryptPasswordEncoder:

@Bean 
public PasswordEncoder passwordEncoder() { 
    return new BCryptPasswordEncoder(); 
}

Tiếp theo, hãy cấu hình cho HttpSecurity.

3.2. Cấu hình để authorize request

Chúng ta bắt đầu bằng cách thực hiện các cấu hình cần thiết để authorize request.

Tại đây, chúng ta đang cho phép anonymous ( người ẩn danh) truy cập /login để user có thể authenticate. /admin chỉ dành cho user có role ADMIN và đảm bảo mọi thứ khác:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
      .csrf().disable()
      .authorizeRequests()
      .antMatchers("/admin/**").hasRole("ADMIN")
      .antMatchers("/anonymous*").anonymous()
      .antMatchers("/login*").permitAll()
      .anyRequest().authenticated()
      .and()
      // ...
}

Lưu ý rằng thứ tự của các phần tử antMatchers ()  là rất quan trọng - các quy tắc cụ thể hơn cần đến trước, sau đó là các quy tắc chung chung hơn.


3.3. Cấu hình cho Form đăng nhập

Tiếp theo, chúng ta mở rộng đoạn code configure ở trên cho form đăng nhập và đăng xuất:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
      // ...
      .and()
      .formLogin()
      .loginPage("/login.html")
      .loginProcessingUrl("/perform_login")
      .defaultSuccessUrl("/homepage.html", true)
      .failureUrl("/login.html?error=true")
      .failureHandler(authenticationFailureHandler())
      .and()
      .logout()
      .logoutUrl("/perform_logout")
      .deleteCookies("JSESSIONID")
      .logoutSuccessHandler(logoutSuccessHandler());
}
  • loginPage()  - trang đăng nhập tùy chỉnh
  • loginProcessingUrl() - URL để submit username và password
  • defaultSuccessUrl() - trang đích sau khi đăng nhập thành công
  • failUrl() - trang đích sau khi đăng nhập không thành công
  • logoutUrl() - đăng xuất tùy chỉnh

4. Thêm Spring Security vào Ứng dụng Web

Để sử dụng cấu hình Spring Security được xác định ở trên, chúng ta cần đính kèm nó vào ứng dụng web.

Chúng ta sẽ sử dụng WebApplicationInitializer , vì vậy chúng ta không cần cung cấp bất kỳ web.xml nào :

public class AppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext sc) {

        AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
        root.register(SecSecurityConfig.class);

        sc.addListener(new ContextLoaderListener(root));

        sc.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
          .addMappingForUrlPatterns(null, false, "/*");
    }
}

Lưu ý rằng trình khởi tạo này không cần thiết nếu chúng ta đang sử dụng ứng dụng Spring Boot. Hãy xem bài viết của chúng tôi về cấu hình tự động bảo mật Spring Boot để biết thêm chi tiết về cách cấu hình bảo mật được load trong Spring Boot.

 

5. Cấu hình XML Spring Security

Hãy cùng xem xét cấu hình XML tương ứng.

Dự án tổng thể đang sử dụng cấu hình Java, vì vậy chúng ta cần import file cấu hình XML qua một class Java @Configuration :

@Configuration
@ImportResource({ "classpath:webSecurityConfig.xml" })
public class SecSecurityConfig {
   public SecSecurityConfig() {
      super();
   }
}

Và file cấu hình XML Spring Security - webSecurityConfig.xml :

<http use-expressions="true">
    <intercept-url pattern="/login*" access="isAnonymous()" />
    <intercept-url pattern="/**" access="isAuthenticated()"/>

    <form-login login-page='/login.html' 
      default-target-url="/homepage.html" 
      authentication-failure-url="/login.html?error=true" />
    <logout logout-success-url="/login.html" />
</http>

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user1" password="user1Pass" authorities="ROLE_USER" />
        </user-service>
        <password-encoder ref="encoder" />
    </authentication-provider>
</authentication-manager>

<beans:bean id="encoder" 
  class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</beans:bean>

 

6. Web.xml

Trước phần giới thiệu của Spring 4 , chúng tôi đã từng cấu hình Spring Security trong web.xml  - chỉ một bộ lọc bổ sung được thêm vào Spring MVC web.xml chuẩn :

<display-name>Spring Secured Application</display-name>

<!-- Spring MVC -->
<!-- ... -->

<!-- Spring Security -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Bộ lọc - DelegateFilterProxy - đơn giản ủy quyền cho một bean do Spring quản lý - FilterChainProxy - mà bản thân nó có thể hưởng lợi từ việc quản lý toàn bộ vòng đời của Spring bean.

 

7. Form đăng nhập

Trang form đăng nhập sẽ được đăng ký với Spring MVC bằng cách sử dụng cơ chế đơn giản để ánh xạ tên views thành URL mà không cần controller rõ ràng ở giữa:

registry.addViewController("/login.html");

Tất nhiên, điều này tương ứng với login.jsp :

<html>
<head></head>
<body>
   <h1>Login</h1>
   <form name='f' action="login" method='POST'>
      <table>
         <tr>
            <td>User:</td>
            <td><input type='text' name='username' value=''></td>
         </tr>
         <tr>
            <td>Password:</td>
            <td><input type='password' name='password' /></td>
         </tr>
         <tr>
            <td><input name="submit" type="submit" value="submit" /></td>
         </tr>
      </table>
  </form>
</body>
</html>

Form đăng nhập của Spring có các điều liên quan sau:

  • login - URL nơi form được ĐĂNG để kích hoạt quá trình authenticate
  • username - tên người dùng
  • password - mật khẩu

8. Cấu hình thêm cho Spring Login

Chúng ta đã thảo luận ngắn gọn về một số cấu hình của cơ chế đăng nhập khi chúng ta giới thiệu Cấu hình Spring Security ở trên - bây giờ chúng ta hãy đi vào một số chi tiết.

Một lý do để override hầu hết các mặc định trong Spring Security là để ẩn đi thực tế rằng ứng dụng được bảo mật bằng Spring Security và giảm thiểu thông tin mà kẻ tấn công tiềm năng biết về ứng dụng.

Được cấu hình đầy đủ, phần tử đăng nhập trông như thế này:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
      .loginPage("/login.html")
      .loginProcessingUrl("/perform_login")
      .defaultSuccessUrl("/homepage.html",true)
      .failureUrl("/login.html?error=true")
}

Hay cấu hình XML tương ứng:

<form-login 
  login-page='/login.html' 
  login-processing-url="/perform_login" 
  default-target-url="/homepage.html"
  authentication-failure-url="/login.html?error=true" 
  always-use-default-target="true"/>

 

8.1. Trang đăng nhập

Tiếp theo, hãy xem cách chúng ta có thể cấu hình trang đăng nhập tùy chỉnh bằng phương thức loginPage ():

http.formLogin()
  .loginPage("/login.html")

Hoặc, thông qua cấu hình XML:

login-page='/login.html'

Nếu chúng ta không chỉ định điều này, Spring Security sẽ tạo một Form đăng nhập rất cơ bản tại URL /login .
 

8.2. URL POST để Đăng nhập

URL mặc định nơi Spring Login sẽ POST để kích hoạt quy trình authenticate là /login mà từng là /j_spring_security_check trước lúc Spring Security 4.

Chúng ta có thể sử dụng phương thức loginProcessingUrl để override URL này:

http.formLogin()
  .loginProcessingUrl("/perform_login")

Hoặc, thông qua cấu hình XML:

login-processing-url="/perform_login"

Một lý do chính đáng để override URL mặc định này là để ẩn đi thực tế rằng ứng dụng thực sự được bảo mật bằng Spring Security - thông tin đó sẽ không có sẵn bên ngoài.
 

8.3. Trang đích thành công

Sau khi quá trình đăng nhập thành công, user được chuyển hướng đến một trang - theo mặc định là gốc rễ của ứng dụng web.

Chúng ta có thể override trang này thông qua phương thức defaultSuccessUrl() :

http.formLogin()
  .defaultSuccessUrl("/homepage.html")

Hoặc với cấu hình XML:

default-target-url="/homepage.html"

Trong trường hợp always-use-default-target được đặt thành true, thì user luôn được chuyển hướng đến trang này. Nếu thuộc tính đó được đặt thành false, thì người dùng sẽ được chuyển đến trang trước đó mà họ muốn truy cập trước khi được nhắc authenticate.
 

8.4. Trang đích bị lỗi

Tương tự như với Trang Đăng Nhập, Trang Đăng Nhập Thất Bại sẽ tự động tạo ra bởi Spring Security tại / login?error theo mặc định.

Để override trang này, chúng ta có thể sử dụng  phương thức failUrl () :

http.formLogin()
  .failureUrl("/login.html?error=true")

Hoặc với XML:

authentication-failure-url="/login.html?error=true"

 

9. Kết luận

Trong Ví dụ Spring Login này , chúng ta đã cấu hình một quy trình authenticate đơn giản - chúng ta đã thảo luận về Form đăng nhập Spring Security, Cấu hình bảo mật và một số điều chỉnh nâng cao hơn có sẵn.

Việc triển khai bài giảng Spring Login này có thể được tìm thấy trong dự án GitHub - đây là một dự án dựa trên Eclipse, vì vậy nó sẽ dễ dàng import và chạy như vốn có.

Khi dự án chạy cục bộ, HTML mẫu có thể được truy cập tại:

http://localhost:8080/spring-security-mvc-login/login.html