Trước tiên tôi muốn bình luận mà tôi đã kiểm tra các câu hỏi khác trong Stack Overflow và thực hiện cách tiếp cận riêng của tôi dựa trên câu trả lời: https://stackoverflow.com/a/14425801/2487263 và https://stackoverflow.com/a/16101649/2487263Làm cách nào để xử lý các ngoại lệ xác thực khác nhau trong bảo mật Mùa xuân 3.1?
Tôi cố gắng để đảm bảo một API REST sử dụng bảo mật mùa xuân 3.1 trong một Mùa xuân 3.2, ứng dụng spring MVC, tôi đang sử dụng một cách tiếp cận xác thực cơ bản với một cấu hình đơn giản:
<http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint">
<intercept-url pattern="/**" access="ROLE_USER"/>
<http-basic />
</http>
Như bạn có thể thấy tôi đang sử dụng điểm vào tùy chỉnh của tôi tôi có đối tượng ErrorResponse của riêng tôi mà tôi sẽ thêm vào http phản hồi ở định dạng json, xem mã bên dưới:
@Component
public class AuthenticationFailedEntryPoint implements AuthenticationEntryPoint {
static Logger log = Logger.getLogger(AuthenticationFailedEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.error(ExceptionUtils.getStackTrace(authException));
ErrorResponse errorResponse = new ErrorResponse();
... here I fill my errorResponse object ...
ObjectMapper jsonMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(status);
PrintWriter out = response.getWriter();
out.print(jsonMapper.writeValueAsString(errorResponse));
}
}
tôi đã cố gắng tiếp cận với hai trường hợp thử nghiệm:
- Cố gắng tiêu thụ một dịch vụ mà không cung cấp tiêu đề xác thực cơ bản:
Đây là request/response:
GET http://localhost:8081/accounts/accounts?accountNumber=1013
-- response --
401 Unauthorized
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 320
Date: Fri, 25 Oct 2013 17:11:15 GMT
Proxy-Connection: Keep-alive
{"status":401,"messages":[{"code":"000011","message":"You are not authorized to reach this endpoint"}]}
2.- Hãy thử sử dụng cùng một dịch vụ nhưng giờ gửi cơ bản đích thực header ation với một mật khẩu sai:
Đây là request/response:
GET http://localhost:8081/accounts/accounts?accountNumber=1013
Authorization: Basic bXl1c2VyOmdvb2RieWU=
-- response --
401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Basic realm="Spring Security Application"
Content-Type: text/html;charset=utf-8
Content-Length: 1053
Date: Fri, 25 Oct 2013 17:03:09 GMT
Proxy-Connection: Keep-alive
<html> ... ugly html generated by tc server ... </html>
Như bạn có thể thấy trong trường hợp đầu tiên các điểm nhập cảnh đã đạt được và phương pháp Bắt được thực hiện với việc xử lý đúng đắn của ngoại lệ và trả lời json đã được trả về. Nhưng điều đó không xảy ra khi mật khẩu sai.
Trong các bản ghi Tôi thấy rằng cả hai trường hợp đang sản xuất một dòng chảy khác nhau:
Đối với trường hợp 1 (không có tiêu đề auth):
...
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /accounts?accountNumber=1013; Attributes: [ROLE_USER]
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.sprin[email protected]9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.access.vote.AffirmativeBased - Voter: [email protected], returned: -1
2013-10-25 13:11:15,831 DEBUG tomcat-http--13 org.springframework.security.access.vote.AffirmativeBased - Voter: [email protected]ef7, returned: 0
2013-10-25 13:11:15,831 DEBUG tomcat-http--13 org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
...
Đối với trường hợp 2 (mật khẩu sai):
...
2013-10-25 13:03:08,941 DEBUG tomcat-http--11 org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'myuser'
2013-10-25 13:03:08,941 DEBUG tomcat-http--11 org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2013-10-25 13:03:09,544 DEBUG tomcat-http--11 org.springframework.security.authentication.dao.DaoAuthenticationProvider - Authentication failed: password does not match stored value
2013-10-25 13:03:09,545 DEBUG tomcat-http--11 org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-10-25 13:00:30,136 DEBUG tomcat-http--9 org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
...
Trường hợp đầu tiên ném AccessDeniedException đang bị bắt và gửi đến phương thức bắt đầu trong điểm vào của tôi, nhưng trường hợp thứ hai ném BadCredentialsException sẽ không đi o điểm vào.
Điều lạ ở đây là phương pháp Bắt được coi nhận được một AuthenticationException, nhưng AccessDeniedException không phải là một AuthenticationException nhưng BadCredentialsException là, xem cây thừa kế từ an ninh mùa xuân tài liệu 3.1 API:
java.lang.Object
extended by java.lang.Throwable
extended by java.lang.Exception
extended by java.lang.RuntimeException
extended by org.springframework.security.access.AccessDeniedException
java.lang.Object
extended by java.lang.Throwable
extended by java.lang.Exception
extended by java.lang.RuntimeException
extended by org.springframework.security.core.AuthenticationException
extended by org.springframework.security.authentication.BadCredentialsException
Tại sao phương pháp bắt đầu được gọi với ngoại lệ không phải là loại chính xác và tại sao không được gọi khi có BadCredentialsException thuộc loại đúng?
Sửa --- Triển khai công tác câu trả lời bằng @Luke
Hai mô tả các giải pháp sử dụng AuthenticationEntryPoint tùy chỉnh hiển thị trong câu hỏi, cấu hình cần phải được sửa đổi lựa chọn một trong hai tùy chọn sau:
Thêm một BASIC_AUTH_FILTER tùy chỉnh:
<http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint"> <intercept-url pattern="/**" access="ROLE_USER"/> <custom-filter position="BASIC_AUTH_FILTER" ref="authenticationFilter" /> </http> <beans:bean id="authenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter"> <beans:constructor-arg name="authenticationManager" ref="authenticationManager" /> <beans:constructor-arg name="authenticationEntryPoint" ref="authenticationFailedEntryPoint" /> </beans:bean>
HOẶC Thêm điểm mấu chốt để các yếu tố http-cơ bản, IMO là giải pháp sạch:
<http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint"> <intercept-url pattern="/**" access="ROLE_USER"/> <http-basic entry-point-ref="authenticationFailedEntryPoint" /> </http>
Về vấn đề ngoại lệ được nhận xét ở cuối câu hỏi, có vẻ như AccesDeniedException đang được ExceptionTranlationFilter dịch sang một AuthenticationException. Đó là lý do tại sao phương thức bắt đầu trong điểm nhập tùy chỉnh được gọi là – raspacorp
raspacorp - Làm thế nào để thực hiện phương thức "bắt đầu" mặc dù chúng ta có @ExceptionHandler tại chỗ? – Prateek
@pashtika Đây là một vấn đề mà tôi đã có gần hai năm trước và được giải quyết bằng cách sử dụng điểm nhập cảnh tùy chỉnh đó. Tôi nhớ rằng các trình xử lý ngoại lệ đã không bắt được ngoại lệ đó vì nó xảy ra trong một bước đầu. Tuy nhiên tôi không biết nếu đó là cố định trong các phiên bản gần đây của khuôn khổ, trong trường hợp chỉ cần thêm một xử lý ngoại lệ thích hợp sẽ làm các trick. – raspacorp