Chuỗi bộ lọc bảo mật Spring là một công cụ rất phức tạp và linh hoạt.
Các bộ lọc chính trong chuỗi là (theo thứ tự)
- SecurityContextPersistenceFilter (khôi phục Xác thực từ JSESSIONID)
- UsernamePasswordAuthenticationFilter (thực hiện xác thực)
- ExceptionTranslationFilter (bắt ngoại lệ bảo mật từ FilterSecurityInterceptor)
- FilterSecurityInterceptor (có thể ném ngoại lệ xác thực và ủy quyền)
Nhìn vào tài liệu phát hành ổn định 4.2.1 hiện tại , phần 13.3 Đặt hàng bộ lọc, bạn có thể thấy toàn bộ tổ chức bộ lọc của chuỗi bộ lọc:
13.3 Đặt hàng bộ lọc
Thứ tự mà các bộ lọc được xác định trong chuỗi là rất quan trọng. Bất kể bạn đang sử dụng bộ lọc nào, thứ tự sẽ như sau:
ChannelProcessingFilter , vì có thể cần phải chuyển hướng đến một giao thức khác
SecurityContextPersistenceFilter , do đó SecurityContext có thể được thiết lập trong SecurityContextHolder khi bắt đầu yêu cầu web và mọi thay đổi đối với SecurityContext có thể được sao chép sang httpSession khi yêu cầu web kết thúc (sẵn sàng để sử dụng với yêu cầu web tiếp theo)
Đồng thờiSessionFilter , vì nó sử dụng chức năng SecurityContextHolder và cần cập nhật SessionRegistry để phản ánh các yêu cầu đang diễn ra từ hiệu trưởng
Các cơ chế xử lý xác thực -
UsernamePasswordAuthenticationFilter , CasAuthenticationFilter , BasicAuthenticationFilter ,
v.v. - để SecurityContextHolder có thể được sửa đổi để chứa mã thông báo yêu cầu xác thực hợp lệ
Các SecurityContextHolderAwareRequestFilter , nếu bạn đang sử dụng nó để cài đặt một mùa xuân an ninh biết HttpServletRequestWrapper vào container servlet của bạn
Các JaasApiIntegrationFilter , nếu một JaasAuthenticationToken là trong SecurityContextHolder này sẽ xử lý FilterChain như Chủ đề trong JaasAuthenticationToken
Ghi nhớ MeAuthenticationFilter , để nếu không có cơ chế xử lý xác thực trước đó cập nhật SecurityContextHolder và yêu cầu xuất hiện một cookie cho phép các dịch vụ ghi nhớ diễn ra, một đối tượng Xác thực được ghi nhớ phù hợp sẽ được đặt ở đó
AnonymousAuthenticationFilter , để nếu không có cơ chế xử lý xác thực trước đó cập nhật SecurityContextHolder, một đối tượng Xác thực ẩn danh sẽ được đặt ở đó
ExceptionTranslationFilter , để bắt bất kỳ trường hợp ngoại lệ nào của Spring Security để có thể trả về phản hồi lỗi HTTP hoặc xác thực phù hợpEntryPoint có thể được khởi chạy
FilterSecurityInterceptor , để bảo vệ các URI web và nâng cao ngoại lệ khi quyền truy cập bị từ chối
Bây giờ, tôi sẽ cố gắng tiếp tục từng câu hỏi của bạn:
Tôi bối rối không biết các bộ lọc này được sử dụng như thế nào. Có phải là cho mùa xuân cung cấp mẫu đăng nhập, UsernamePasswordAuthenticationFilter chỉ được sử dụng cho / đăng nhập, còn các bộ lọc sau thì không? Phần tử không gian tên đăng nhập biểu mẫu có tự động cấu hình các bộ lọc này không? Có phải mọi yêu cầu (được xác thực hay không) đều đạt đến FilterSecurityInterceptor cho url không đăng nhập?
Khi bạn đang định cấu hình một <security-http>
phần, đối với mỗi phần, bạn ít nhất phải cung cấp một cơ chế xác thực. Đây phải là một trong những bộ lọc khớp với nhóm 4 trong phần Đặt hàng bộ lọc 13.3 từ tài liệu Bảo mật mùa xuân mà tôi vừa tham khảo.
Đây là bảo mật hợp lệ tối thiểu: phần tử http có thể được cấu hình:
<security:http authentication-manager-ref="mainAuthenticationManager"
entry-point-ref="serviceAccessDeniedHandler">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
</security:http>
Chỉ cần làm điều đó, các bộ lọc này được cấu hình trong proxy chuỗi bộ lọc:
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"6": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"7": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"8": "org.springframework.security.web.session.SessionManagementFilter",
"9": "org.springframework.security.web.access.ExceptionTranslationFilter",
"10": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
Lưu ý: Tôi nhận được chúng bằng cách tạo một RestContoder đơn giản mà @Autowires FilterChainProxy và trả về nội dung của nó:
@Autowired
private FilterChainProxy filterChainProxy;
@Override
@RequestMapping("/filterChain")
public @ResponseBody Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
return this.getSecurityFilterChainProxy();
}
public Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
Map<Integer, Map<Integer, String>> filterChains= new HashMap<Integer, Map<Integer, String>>();
int i = 1;
for(SecurityFilterChain secfc : this.filterChainProxy.getFilterChains()){
//filters.put(i++, secfc.getClass().getName());
Map<Integer, String> filters = new HashMap<Integer, String>();
int j = 1;
for(Filter filter : secfc.getFilters()){
filters.put(j++, filter.getClass().getName());
}
filterChains.put(i++, filters);
}
return filterChains;
}
Ở đây chúng ta có thể thấy rằng chỉ bằng cách khai báo <security:http>
phần tử với một cấu hình tối thiểu, tất cả các bộ lọc mặc định đều được bao gồm, nhưng không có bộ lọc nào thuộc loại Xác thực (nhóm thứ 4 trong phần Đặt hàng Bộ lọc 13.3). Vì vậy, nó thực sự có nghĩa là chỉ bằng cách khai báo security:http
phần tử, SecurityContextPersistenceFilter, ExceptionTranslationFilter và FilterSecurityInterceptor được cấu hình tự động.
Trong thực tế, một cơ chế xử lý xác thực nên được cấu hình và thậm chí các yêu cầu xử lý không gian tên bảo mật cho điều đó, gây ra lỗi trong khi khởi động, nhưng có thể bỏ qua việc thêm thuộc tính ref-point-ref <http:security>
Nếu tôi thêm một cơ bản <form-login>
vào cấu hình, theo cách này:
<security:http authentication-manager-ref="mainAuthenticationManager">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
<security:form-login />
</security:http>
Bây giờ, bộ lọcChain sẽ như thế này:
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter",
"6": "org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter",
"7": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"8": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"9": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"10": "org.springframework.security.web.session.SessionManagementFilter",
"11": "org.springframework.security.web.access.ExceptionTranslationFilter",
"12": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
Bây giờ, điều này hai bộ lọc org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter và org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter được tạo ra và cấu hình trong FilterChainProxy.
Vì vậy, bây giờ, các câu hỏi:
Có phải là cho mùa xuân cung cấp mẫu đăng nhập, UsernamePasswordAuthenticationFilter chỉ được sử dụng cho / đăng nhập, còn các bộ lọc sau thì không?
Có, nó được sử dụng để cố gắng hoàn thành cơ chế xử lý đăng nhập trong trường hợp yêu cầu khớp với url UsernamePasswordAuthenticationFilter. Url này có thể được cấu hình hoặc thậm chí thay đổi hành vi của nó để phù hợp với mọi yêu cầu.
Bạn cũng có thể có nhiều hơn một cơ chế xử lý Xác thực được định cấu hình trong cùng một FilterchainProxy (chẳng hạn như HttpBasic, CAS, v.v.).
Phần tử không gian tên đăng nhập biểu mẫu có tự động cấu hình các bộ lọc này không?
Không, phần tử đăng nhập biểu mẫu định cấu hình UsernamePasswordAUthenticationFilter và trong trường hợp bạn không cung cấp url trang đăng nhập, nó cũng cấu hình org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter trang.
Các bộ lọc khác được cấu hình tự động theo mặc định chỉ bằng cách tạo một <security:http>
phần tử không có security:"none"
thuộc tính.
Có phải mọi yêu cầu (được xác thực hay không) đều đạt đến FilterSecurityInterceptor cho url không đăng nhập?
Mọi yêu cầu phải đạt được nó, vì đó là yếu tố quan tâm đến việc yêu cầu đó có quyền truy cập url được yêu cầu hay không. Nhưng một số bộ lọc được xử lý trước đó có thể dừng xử lý chuỗi bộ lọc mà không gọi FilterChain.doFilter(request, response);
. Ví dụ: bộ lọc CSRF có thể dừng xử lý chuỗi bộ lọc nếu yêu cầu không có tham số csrf.
Điều gì xảy ra nếu tôi muốn bảo mật API REST của mình bằng mã thông báo JWT, được lấy từ đăng nhập? Tôi phải cấu hình hai thẻ http cấu hình không gian tên, quyền? Một cái khác cho / đăng nhập với UsernamePasswordAuthenticationFilter
, và một cái khác cho url của REST, với tùy chỉnh JwtAuthenticationFilter
.
Không, bạn không bị buộc phải làm theo cách này. Bạn có thể khai báo cả hai UsernamePasswordAuthenticationFilter
và JwtAuthenticationFilter
trong cùng một phần tử http, nhưng nó phụ thuộc vào hành vi cụ thể của từng bộ lọc này. Cả hai cách tiếp cận đều có thể, và lựa chọn nào để lựa chọn tài chính tùy thuộc vào sở thích riêng.
Việc cấu hình hai phần tử http có tạo ra hai springSecurityFitlerChains không?
Vâng đó là sự thật
UsernamePasswordAuthenticationFilter bị tắt theo mặc định, cho đến khi tôi khai báo mẫu đăng nhập?
Có, bạn có thể thấy nó trong các bộ lọc được nâng lên trong mỗi cấu hình tôi đã đăng
Làm cách nào để thay thế SecurityContextPersistenceFilter bằng một cái, cái này sẽ có được Xác thực từ mã thông báo JWT hiện tại thay vì JSESSIONID?
Bạn có thể tránh SecurityContextPersistenceFilter, chỉ cần cấu hình chiến lược phiên trong <http:element>
. Chỉ cần cấu hình như thế này:
<security:http create-session="stateless" >
Hoặc, trong trường hợp này, bạn có thể ghi đè lên nó bằng một bộ lọc khác, theo cách này bên trong <security:http>
phần tử:
<security:http ...>
<security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>
</security:http>
<beans:bean id="myCustomFilter" class="com.xyz.myFilter" />
BIÊN TẬP:
Một câu hỏi về "Bạn cũng có thể có nhiều hơn một cơ chế xử lý Xác thực được định cấu hình trong cùng một FilterchainProxy". Cái sau sẽ ghi đè xác thực được thực hiện bởi cái đầu tiên, nếu khai báo nhiều bộ lọc xác thực (thực hiện Spring)? Làm thế nào điều này liên quan đến việc có nhiều nhà cung cấp xác thực?
Điều này cuối cùng phụ thuộc vào việc thực hiện từng bộ lọc, nhưng thực tế là các bộ lọc xác thực sau ít nhất có thể ghi đè lên bất kỳ xác thực trước đó cuối cùng được thực hiện bởi các bộ lọc trước đó.
Nhưng điều này sẽ không xảy ra. Tôi có một số trường hợp sản xuất trong các dịch vụ REST được bảo mật trong đó tôi sử dụng một loại mã thông báo ủy quyền có thể được cung cấp cả dưới dạng tiêu đề http hoặc bên trong thân yêu cầu. Vì vậy, tôi định cấu hình hai bộ lọc phục hồi mã thông báo đó, trong một trường hợp từ Tiêu đề http và bộ lọc khác từ thân yêu cầu của yêu cầu nghỉ ngơi riêng. Đúng là nếu một yêu cầu http cung cấp mã thông báo xác thực đó dưới dạng tiêu đề http và bên trong thân yêu cầu, cả hai bộ lọc sẽ cố gắng thực thi cơ chế xác thực ủy quyền cho người quản lý, nhưng có thể dễ dàng tránh được việc kiểm tra xem yêu cầu có phải là dễ dàng không đã được xác thực ngay khi bắt đầu doFilter()
phương thức của từng bộ lọc.
Có nhiều bộ lọc xác thực có liên quan đến việc có nhiều hơn một nhà cung cấp xác thực, nhưng đừng ép buộc. Trong trường hợp tôi tiếp xúc trước đây, tôi có hai bộ lọc xác thực nhưng tôi chỉ có một nhà cung cấp xác thực, vì cả hai bộ lọc đều tạo cùng một loại đối tượng Xác thực nên trong cả hai trường hợp, trình quản lý xác thực ủy quyền cho cùng một nhà cung cấp.
Và ngược lại với điều này, tôi cũng có một kịch bản khi tôi chỉ xuất bản một UsernamePasswordAuthenticationFilter nhưng cả hai thông tin người dùng đều có thể được chứa trong DB hoặc LDAP, vì vậy tôi có hai nhà cung cấp hỗ trợ UsernamePasswordAuthenticationToken và ủy quyền cho nhà cung cấp xác thực bảo mật để xác thực các thông tin.
Vì vậy, tôi nghĩ rõ ràng rằng cả số lượng bộ lọc xác thực không xác định số lượng nhà cung cấp xác thực cũng như số lượng nhà cung cấp xác định số lượng bộ lọc.
Ngoài ra, tài liệu nêu rõ SecurityContextPersistenceFilter chịu trách nhiệm làm sạch SecurityContext, đây là nhóm chủ đề quan trọng. Nếu tôi bỏ qua nó hoặc cung cấp thực hiện tùy chỉnh, tôi phải thực hiện việc làm sạch bằng tay, phải không? Có nhiều gotcha tương tự khi tùy chỉnh chuỗi không?
Tôi đã không xem xét kỹ bộ lọc này trước đây, nhưng sau câu hỏi cuối cùng của bạn, tôi đã kiểm tra việc triển khai và như thường lệ trong Spring, gần như mọi thứ đều có thể được định cấu hình, mở rộng hoặc ghi đè.
Các đại biểu SecurityContextPersistenceFilter trong triển khai SecurityContextRep repository tìm kiếm SecurityContext. Theo mặc định, một httpSessionSecurityContextRep repository được sử dụng, nhưng điều này có thể được thay đổi bằng cách sử dụng một trong các hàm tạo của bộ lọc. Vì vậy, tốt hơn là bạn nên viết SecurityContextRep repository phù hợp với nhu cầu của bạn và chỉ cần cấu hình nó trong SecurityContextPersistenceFilter, tin tưởng vào hành vi đã được chứng minh của nó thay vì bắt đầu thực hiện từ đầu.