Bảo mật ứng dụng Java web bởi Spring Security

Bài viết được sự cho phép của smartjob.vn
Chúng mình xin chia sẻ với bạn kỹ thuật bảo mật tiên tiến, mạnh mẽ dành cho Java web sử dụng Spring Security. Bài viết hội tụ các best-practice, các kỹ thuật tối tân nhất hiện nay. Nội dung sẽ hay và khó.
Công nghệ sử dụng (technology-stack)
- Spring Security
- Spring Boot
- Spring Web MVC
- Gradle (build system)
- Thymeleaf (template engine)
Công cụ sử dụng
- IntelliJ IDEA 2016.1.3
- Apache Tomcat 8.0.35
Trước khi bắt tay thực hiện, Bạn nên đọc các bài viết trên SmartJob:
- Giới thiệu Spring Boot (để hiểu về Spring Boot)
- Kiểm tra tính hợp lệ của dữ liệu đầu vào form Spring Web MVC bởi Hibernate Validator (để hiểu về luồng đi của Spring Web MVC)
Spring Security [1] là bộ khung bảo mật ứng dụng Java web cung cấp cơ chế cấp phép quyền (authorization) và xác thực người dùng (authentication). Sức mạnh của Spring Security thể hiện ở sự dễ dùng, dễ cấu hình, khả năng mở rộng, và khả năng bảo mật ứng dụng mạnh mẽ. Spring Security chống được các kỹ thuật hacking tinh vi:
- Session fixation (Tấn công chiếm quyền điều khiển session của người dùng) [2]
- Clickjacking (click chuột tự động, ví dụ click vào nút Like Facebook mà không xin phép người dùng)
- CSRF (Cross-site request forgery: Tạo truy vấn (request) giả mạo truyền từ trang này sang trang khác)
Mục tiêu
Chúng ta sẽ xây dựng ứng dụng Java web có trang chủ, trang đăng nhập, khu vực bảo mật (chỉ truy cập được sau khi người dùng hợp lệ đăng nhập). Cấu trúc project sau khi hoàn tất sẽ như thế này
Bạn sẽ phải tạo/chỉnh sửa 7 file:
- Application.java
- MvcConfig.java
- WebSecurityConfig.java
- dang_nhap.html
- khu_vuc_bao_mat.html
- trang_chu.html
- build.gradle
Sử dụng IntelliJ IDEA để khởi tạo project:
Bộ khung project được dựng trên nền Spring Boot. Bấm chọn Spring Initializr, rồi bấm Next.
Bạn điền thông tin, các tùy chọn vào 10 mục như ảnh chụp màn hình. Bước thứ 11 là bấm nút Next
Chọn và nhặt vào thư viện cần dùng. 3 thành phần tham gia mở rộng ứng dụng Spring Boot là:
- Security
- Web
- Thymeleaf
Phiên bản Spring Boot ổn định, mới nhất tại thời điểm viết bài là 1.3.5.RELEASE
Đặt tên project, chọn vị trí lưu mã nguồn project
Cấu hình cho Gradle build system
InteliJ IDEA và Spring Initializer tự động sinh ra cho bạn cấu trúc thư mục của project. Tìm file build.gradle sửa lại nội dung như sau:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.5.RELEASE")
}
}
apply plugin: 'war'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'demo_spring_security'
version = '1.0.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-starter-security")
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
testCompile("junit:junit")
}
Dòng 10, 29 cho phép chúng ta đóng gói ứng dụng Spring Boot thành file WAR truyền thống. (Nếu để theo mặc định, Spring Boot sẽ đóng gói ứng dụng web thành file JAR thực thi dùng Apache Tomcat nhúng, run trực tiếp, rất đặc sắc. Tuy nhiên theo cách này việc deploy sẽ quá đơn giản đến mức khiến bạn đọc khó hiểu, tại sao không có file WAR mà ứng dụng Java web vẫn chạy).
Tìm Java job lương cao trên Station D ngay!
Tạo thêm thư mục java trong thư mục src/main, rồi tạo thêm package vn.smartjob.demo_spring.demo_security . Tạo class Application.java
package vn.smartjob.demo_spring.demo_security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
/**
* Điểm đi vào của ứng dụng Spring Boot.
*/
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
public static void main(String[] args) throws Throwable {
SpringApplication.run(Application.class, args);
}
}
Mã nguồn dưới đây thực chất đã chuyển đổi (convert) ứng dụng Spring Boot nguyên bản (tạo ra file JAR có thể run được) thành ứng dụng web Java truyền thống (đóng gói thành file WAR) bằng cách:
– Dòng 12, class Application được extends từ class org.springframework.boot.context.web.SpringBootServletInitializer
– Dòng 14 đến 17, phải Override method configure để đóng gói ứng dụng Spring Boot thành file WAR truyền thống.
Tạo thêm thư mục resources bên trong thư mục srcmain. Tạo thư mục templates bên trong thư mục resources.. Sau đó tạo 3 tập tin giao diện (sử dụng Thymeleaf template engine). Các file giao diện sử dụng Thymeleaf. Ở đâu Thymeleaf xử lý (render) ra giao diện, sẽ sử dụng HTML element mà trong đó chứa thuộc tính th:____ . Các thẻ security chèn vào trong giao diện sẽ do Spring Security xử lý.
File trang_chu.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://wwww.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Trang chủ</title> </head> <body> <h1>Ứng dụng minh họa Spring Security</h1> <p>Bấm vào <a th:href="@{/hello}">link này</a> để đăng nhập.</p> </body> </html>
File dang_nhap.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Đăng nhập</title> </head> <body> <div th:if="${param.logout}">Bạn đã đăng xuất.</div> <form th:action="@{/login}" method="post"> <th:block>Tên đăng nhập</th:block> <th:block><input type="text" name="username"/></th:block> <th:block>Mật khẩu</th:block> <th:block><input type="password" name="password"/></th:block> <th:block><input type="submit" value="Đăng nhập"/></th:block> </form> <div th:if="${param.error}" style="color: red;">Sai tên đăng nhập hoặc sai mật khẩu.</div> </body> </html>
File khu_vuc_bao_mat.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Đây là khu vực bảo mật</title> </head> <body> <h2>Đây là khu vực chỉ dành cho thành viên đã đăng nhập</h2> Bí quyết công nghệ sử dụng trong ứng dụng này:<br/> <ol> <li>Spring Web MVC</li> <li>Spring Boot</li> <li>Spring Security</li> </ol> <h1 th:inline="text">Xin chào bạn [[${#httpServletRequest.remoteUser}]]!</h1> <form th:action="@{/logout}" method="post"> <input type="submit" value="Đăng xuất"/> </form> </body> </html>
File MvcConfig.java . 4 dòng code từ 12 đến 15, là việc ánh xạ (mapping) đường dẫn truy cập với file giao diện tương ứng (đó chính là tên các file html không bao gồm đuôi file).
package vn.smartjob.demo_spring.demo_security; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/home").setViewName("trang_chu"); registry.addViewController("/").setViewName("trang_chu"); registry.addViewController("/hello").setViewName("khu_vuc_bao_mat"); registry.addViewController("/login").setViewName("dang_nhap"); } }
File WebSecurityConfig.java . Cấu hình bảo mật sử dụng hoàn toàn bằng method và class Java. Bạn để ý bên trên class có annotation @Configuration và @EnableWebSecurity để ứng dụng nhận diện thông số bảo mật nằm trong đoạn mã nguồn này. Để cho đơn giản, chúng ta cấp phép 1 người dùng có tên smartjob cùng mật khẩu 4321 . Chúng ta có thể cấu hình các quy tắc bảo mật tinh tế sau này trong các ví dụ sau.
package vn.smartjob.demo_spring.demo_security; import org.springframework.beans.factory.annotation.Autowired; 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; /** * Cấu hình bảo mật */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * Cấu hình xác thực người dùng (authentication). * * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/home").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } /** * Cấu hình cấp phép (quyền) người dùng (authorization). * * @param auth * @throws Exception */ @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("smartjob").password("4321").roles("USER"); } }
Dòng 26: Cấp phép truy cập, khởi tạo luồng lo-gic bảo mật.
Dòng 27: Cho phép tất cả người dùng (đã đăng nhập, hay chưa đăng nhập) đều được truy cập trang chủ.
Dòng 30: Nếu người dùng có nhu cầu, đưa người dùng đến trang Đăng nhập.
Dòng 31: Tất cả mọi người dùng đều có quyền tiếp cận (truy cập) trang Đăng nhập.
Dòng 34: Sau khi đăng nhập, người dùng đã được xác thực có thể Đăng xuất.
Dòng 48: Cấp phép người dùng có tên đăng nhập smartjob, mật khẩu 4321, với vai trò (role) là người dùng thông thường (USER). Trên một hệ thống sẽ có nhiều role khác nhau.
Chạy ứng dụng trên Apache Tomcat 8
Chúng ta cố tình nhập sai tên đăng nhập hoặc mật khẩu để kiểm tra thông báo lỗi và kiểm tra khả năng ngăn chặn của Spring Security:
Khi nhập đúng tên đăng nhập: smartjob và mật khẩu: 4321 thì người dùng sẽ truy cập được khu vực bảo mật:
Đăng nhập thành công, người dùng truy cập được khu vực đặc quyền riêng. Thậm chí Trình duyệt web còn hỏi bạn có muốn lưu tên đăng nhập, mật khẩu ứng dụng trong trình duyệt hay không. Để sử dụng tính năng đăng xuất (do Spring Security cung cấp), bạn bấm nút Đăng xuất để kiểm tra việc vận hành của việc xóa thông tin người dùng trên trình duyệt.
Mẹo: Trong quá trình phát triển ứng dụng, có thể bạn phải xóa thông tin đăng nhập lưu trong trình duyệt đi để test khả năng bảo mật. Bạn làm như sau: Trình duyệt Google Chrome, chọn Setting, gõ từ khóa tìm kiếm pri (privacy)
Chọn Content settings… rồi tìm
Chọn All cookies and site data…
Nhớ bấm nút X rồi sau đó Done để xóa thông tin bảo mật lưu trên trình duyệt, nhằm mục đích chuẩn bị môi trường test ứng dụng Spring Security.
Tải về mã nguồn từ server SmartJob: spring_security hoặc clone/fork từ GitHub: https://github.com/SmartJobVN/spring_security
Bài viết gốc được đăng tải tại smartjob.vn
Có thể bạn quan tâm:
- TOP 10 Web Framework tốt nhất, đáng dùng nhất – Phần 2
- Đa ngôn ngữ ứng dụng Spring Web MVC
- Tài liệu: Expert Oracle and Java Security
Xem thêm Việc làm lập trình Java hấp dẫn trên Station D
Bạn có thể quan tâm
- Tối ưu tỉ lệ chuyển đổi với Google Optimize và Google Analytics(Công Nghệ)
- Hiểu về trình duyệt – How browsers work(Công Nghệ)
- Applicant Tracking System là gì? ATS hoạt động ra sao(Cẩm Nang Tuyển Dụng)