Để có thể thực hiện được việc authentication và authorization các request tới ứng dụng web, Spring Security phải đóng vai trò là interceptor của các ứng dụng này. Nó sẽ chặn, xử lý các request trước khi những request này thực sự được ứng dụng process và trả về kết quả. Trong bài viết này, chúng ta sẽ tìm hiểu xem trước khi request được ứng dụng process thì Spring Security đã làm gì các bạn nhé!
Đầu tiên, để các bạn dễ hình dung, mình sẽ tạo mới ứng dụng Spring MVC hỗ trợ Spring Security để làm ví dụ:
với Spring Security dependencies như sau:
1 2 3 4 5 6 7 8 9 10 11 |
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${org.springframework.security-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${org.springframework.security-version}</version> </dependency> |
1 2 3 4 |
<properties> ... <org.springframework.security-version>5.2.0.RELEASE</org.springframework.security-version> </properties> |
Tập tin security.xml có nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> <http auto-config="true"> <intercept-url pattern="/" access="hasRole('ROLE_USER')"/> </http> <user-service> <user name="khanh" password="{noop}123456" authorities="ROLE_USER" /> </user-service> </beans:beans> |
Tập tin security này định nghĩa user tên “khanh” với role là ROLE_USER có quyền access vào ứng dụng với context path là “/”.
Tất nhiên là các bạn cũng phải cấu hình cho tập tin web.xml để thêm tập tin cấu hình của Spring Security vào các bạn nhé!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- The definition of the Root Spring Container shared by all Servlets and Filters --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/root-context.xml</param-value> </context-param> <!-- Creates the Spring Container shared by all Servlets and Filters --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Processes application requests --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Spring Security Filter --> <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> <!-- Loads Spring Security Configuration File --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/security.xml</param-value> </context-param> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> |
Điều đầu tiên để nói về quy trình xử lý request trong Spring Security là chúng ta phải nói về class DelegatingFilterProxy được khai báo trong tập tin web.xml của ứng dụng. Đây chính là class hiện thực interface javax.servlet.Filter của Java Servlet, đóng vai trò là interceptor, chốt chặn cho mọi request tới ứng dụng Java web. Class này được hiện thực để có thể được quản lý bởi Spring container.
Nếu các bạn đã làm việc với Java Servlet Filter thì sẽ biết là phương thức doFilter() trong interface javax.servlet.Filter này sẽ giúp chúng ta có thể thêm code để handle việc interceptor, class DelegatingFilterProxy cũng sử dụng phương thức doFilter() để làm việc này.
Nếu các bạn xem code của class DelegatingFilterProxy thì sẽ thấy, thật ra trong phương thức doFilter() của class DelegatingFilterProxy, nó chẳng làm gì cả! Nó chỉ delegate việc filter qua một class khác chính là org.springframework.security.web.FilterChainProxy. Class FilterChainProxy cũng hiện thực interface javax.servlet.Filter với phương thức doFilter(). Cụ thể class FilterChainProxy làm gì trong phương thức doFilter() của mình thì tương tự như trong bài viết Tổng quan quy trình xử lý request trong Spring MVC, mình cũng sẽ modify tập tin log4j.xml của project ứng dụng để log ra tất cả những thông tin mà Spring Security thực hiện khi nó authentication và authorization các bạn nhé!
Mình sẽ đổi hết level của các logger cho Spring framework từ info sang debug trong phần “3rdparty Loggers” và root logger phần priority từ warn sang debug. Mình cũng khai báo thêm một logger cho package org.springframework.security của Spring Security.
Lúc này, nội dung của tập tin log4j.xml sẽ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <!-- Appenders --> <appender name="console" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p: %c - %m%n" /> </layout> </appender> <!-- Application Loggers --> <logger name="com.huongdanjava.springsecurityworkflow"> <level value="info" /> </logger> <!-- 3rdparty Loggers --> <logger name="org.springframework.core"> <level value="debug" /> </logger> <logger name="org.springframework.beans"> <level value="debug" /> </logger> <logger name="org.springframework.context"> <level value="debug" /> </logger> <logger name="org.springframework.web"> <level value="debug" /> </logger> <logger name="org.springframework.security"> <level value="debug" /> </logger> <!-- Root Logger --> <root> <priority value="debug" /> <appender-ref ref="console" /> </root> </log4j:configuration> |
Lúc này, nếu chạy ứng dụng và request tới địa chỉ http://localhost:8080, kiểm tra console, các bạn sẽ thấy những dòng log sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 1 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created. DEBUG: org.springframework.security.web.FilterChainProxy - / at position 2 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /' doesn't match 'POST /logout' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /' doesn't match 'POST /login' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 7 of 15 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 8 of 15 in additional filter chain; firing Filter: 'DefaultLogoutPageGeneratingFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/logout' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 9 of 15 in additional filter chain; firing Filter: 'BasicAuthenticationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 10 of 15 in additional filter chain; firing Filter: 'RequestCacheAwareFilter' DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - saved request doesn't match DEBUG: org.springframework.security.web.FilterChainProxy - / at position 11 of 15 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 12 of 15 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter' DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9ec262f5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 13 of 15 in additional filter chain; firing Filter: 'SessionManagementFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 14 of 15 in additional filter chain; firing Filter: 'ExceptionTranslationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 15 of 15 in additional filter chain; firing Filter: 'FilterSecurityInterceptor' DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /; Attributes: [authenticated] DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9ec262f5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@26633508, returned: -1 DEBUG: 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 at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter.doFilterInternal(DefaultLogoutPageGeneratingFilter.java:52) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:206) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1583) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:542) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:536) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1581) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1307) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:482) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1549) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1204) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:221) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:494) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:374) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:268) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:782) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:918) at java.base/java.lang.Thread.run(Thread.java:835) DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request '/' matched by universal pattern '/**' DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - DefaultSavedRequest added to Session: DefaultSavedRequest[http://localhost:8080/] DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Calling Authentication entry point. DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/login' DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@4d606c29 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 1 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: Session@739e3a15{id=node0btk0fs8h0vtwssmqykcwsbl40,x=node0btk0fs8h0vtwssmqykcwsbl40.node0,req=1,res=true}. A new one will be created. DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 2 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /login' doesn't match 'POST /logout' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /login' doesn't match 'POST /login' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 7 of 15 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter' DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@4d606c29 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 1 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: Session@739e3a15{id=node0btk0fs8h0vtwssmqykcwsbl40,x=node0btk0fs8h0vtwssmqykcwsbl40.node0,req=1,res=true}. A new one will be created. DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 2 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /favicon.ico' doesn't match 'POST /logout' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /favicon.ico' doesn't match 'POST /login' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 7 of 15 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 8 of 15 in additional filter chain; firing Filter: 'DefaultLogoutPageGeneratingFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/favicon.ico'; against '/logout' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 9 of 15 in additional filter chain; firing Filter: 'BasicAuthenticationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 10 of 15 in additional filter chain; firing Filter: 'RequestCacheAwareFilter' DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - pathInfo: both null (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - queryString: both null (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - requestURI: arg1=/; arg2=/favicon.ico (property not equals) DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - saved request doesn't match DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 11 of 15 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 12 of 15 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter' DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@a9fd396d: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node0btk0fs8h0vtwssmqykcwsbl40; Granted Authorities: ROLE_ANONYMOUS' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 13 of 15 in additional filter chain; firing Filter: 'SessionManagementFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 14 of 15 in additional filter chain; firing Filter: 'ExceptionTranslationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /favicon.ico at position 15 of 15 in additional filter chain; firing Filter: 'FilterSecurityInterceptor' DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /favicon.ico; Attributes: [authenticated] DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@a9fd396d: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node0btk0fs8h0vtwssmqykcwsbl40; Granted Authorities: ROLE_ANONYMOUS DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@26633508, returned: -1 DEBUG: 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 at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter.doFilterInternal(DefaultLogoutPageGeneratingFilter.java:52) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:206) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1583) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:542) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:536) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1581) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1307) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:482) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1549) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1204) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:221) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:494) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:374) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:268) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:135) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:782) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:918) at java.base/java.lang.Thread.run(Thread.java:835) DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request '/favicon.ico' matched by universal pattern '/**' DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - DefaultSavedRequest added to Session: DefaultSavedRequest[http://localhost:8080/favicon.ico] DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Calling Authentication entry point. DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/login' DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@4d606c29 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 1 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: Session@739e3a15{id=node0btk0fs8h0vtwssmqykcwsbl40,x=node0btk0fs8h0vtwssmqykcwsbl40.node0,req=1,res=true}. A new one will be created. DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 2 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /login' doesn't match 'POST /logout' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /login' doesn't match 'POST /login' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 7 of 15 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter' DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@4d606c29 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed |
Nhìn vào những dòng log trên, các bạn có thể thấy, với config của chúng ta trong tập tin security.xml thì khi gọi tới request vào trang chủ của ứng dụng thì có tất cả 15 class Filter được gọi bởi class FilterChainProxy để Spring Security có thể thực hiện trách nhiệm của mình. Mỗi filter có chức năng và nhiệm vụ riêng, và nếu các bạn xem code của class UsernamePasswordAuthenticationFilter và class BasicAuthenticationFilter, các bạn sẽ thấy 2 class này sẽ đảm nhận cho việc xử lý authentication trong Spring Security. UsernamePasswordAuthenticationFilter sẽ đảm nhận việc authentication khi user gọi đến POST request “/login” còn BasicAuthenticationFilter sẽ đảm nhận việc authentication với thông tin user và password được truyền trên header của request. Cả 2 filter này đều gọi đến phương thức authenticate() của interface AuthenticationManager để thực hiện việc authentication.
Nói thêm về interface AuthenticationManager thì implementation của interface thường dùng nhất là class ProviderManager. Class ProviderManager sẽ gọi một list các AuthenticationProvider mà chúng ta khai báo trong tập tin cấu hình của Spring Security, security.xml :
để thực hiện kiểm tra việc support của các AuthenticationProvider này và thực hiện việc authentication.
Mặc định thì class DaoAuthenticationProvider sẽ đảm nhận việc authentication. Nó sẽ sử dụng class UserDetailsService để lấy thông tin user mà chúng ta khai báo trong tập tin cấu hình security.xml để làm việc này.
Chi tiết của việc authentication thì các bạn đọc thêm code nhé! Mình có thể tóm gọn tổng quan về authentication trong Spring Security như sau:
Để thấy rõ việc authorization xảy ra như thế nào, các bạn hãy thử clear stack trace của IDE và thực hiện việc đăng nhập với username “Khanh” và password “123456” xem sao nhé!
Các bạn sẽ thấy những dòng log sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 1 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: Session@554fe819{id=node01dlwnsmhllu3k1vq2k3wdlfxr00,x=node01dlwnsmhllu3k1vq2k3wdlfxr00.node0,req=1,res=true}. A new one will be created. DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 2 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/logout' DEBUG: org.springframework.security.web.FilterChainProxy - /login at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/login' DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Request is to process authentication DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider DEBUG: org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy - Delegating to org.springframework.security.web.csrf.CsrfAuthenticationStrategy@67922932 DEBUG: org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy - Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@33a4a533 DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bdf25ac6: Principal: org.springframework.security.core.userdetails.User@614935e: Username: khanh; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node01dlwnsmhllu3k1vq2k3wdlfxr00; Granted Authorities: ROLE_USER DEBUG: org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler - Redirecting to DefaultSavedRequest Url: http://localhost:8080/ DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/' DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7a583b0b DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@bdf25ac6: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bdf25ac6: Principal: org.springframework.security.core.userdetails.User@614935e: Username: khanh; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node01dlwnsmhllu3k1vq2k3wdlfxr00; Granted Authorities: ROLE_USER' stored to HttpSession: 'Session@554fe819{id=node01dgz9l49ieu4o1thv3a3u2zega1,x=node01dgz9l49ieu4o1thv3a3u2zega1.node0,req=1,res=true} DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed DEBUG: org.springframework.security.web.FilterChainProxy - / at position 1 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@bdf25ac6: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bdf25ac6: Principal: org.springframework.security.core.userdetails.User@614935e: Username: khanh; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node01dlwnsmhllu3k1vq2k3wdlfxr00; Granted Authorities: ROLE_USER' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 2 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /' doesn't match 'POST /logout' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /' doesn't match 'POST /login' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 7 of 15 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 8 of 15 in additional filter chain; firing Filter: 'DefaultLogoutPageGeneratingFilter' DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/logout' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 9 of 15 in additional filter chain; firing Filter: 'BasicAuthenticationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 10 of 15 in additional filter chain; firing Filter: 'RequestCacheAwareFilter' DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - pathInfo: both null (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - queryString: both null (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - requestURI: arg1=/; arg2=/ (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - serverPort: arg1=8080; arg2=8080 (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - requestURL: arg1=http://localhost:8080/; arg2=http://localhost:8080/ (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - scheme: arg1=http; arg2=http (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - serverName: arg1=localhost; arg2=localhost (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - contextPath: arg1=; arg2= (property equals) DEBUG: org.springframework.security.web.savedrequest.DefaultSavedRequest - servletPath: arg1=/; arg2=/ (property equals) DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - Removing DefaultSavedRequest from session if present DEBUG: org.springframework.security.web.FilterChainProxy - / at position 11 of 15 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 12 of 15 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter' DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bdf25ac6: Principal: org.springframework.security.core.userdetails.User@614935e: Username: khanh; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node01dlwnsmhllu3k1vq2k3wdlfxr00; Granted Authorities: ROLE_USER' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 13 of 15 in additional filter chain; firing Filter: 'SessionManagementFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 14 of 15 in additional filter chain; firing Filter: 'ExceptionTranslationFilter' DEBUG: org.springframework.security.web.FilterChainProxy - / at position 15 of 15 in additional filter chain; firing Filter: 'FilterSecurityInterceptor' DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /; Attributes: [authenticated] DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bdf25ac6: Principal: org.springframework.security.core.userdetails.User@614935e: Username: khanh; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node01dlwnsmhllu3k1vq2k3wdlfxr00; Granted Authorities: ROLE_USER DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@6522dd1b, returned: 1 DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Authorization successful DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - RunAsManager did not change Authentication object DEBUG: org.springframework.security.web.FilterChainProxy - / reached end of additional filter chain; proceeding with original chain DEBUG: org.springframework.web.servlet.DispatcherServlet - GET "/", parameters={} DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to public java.lang.String com.huongdanjava.springsecurityworkflow.HomeController.home(java.util.Locale,org.springframework.ui.Model) INFO : com.huongdanjava.springsecurityworkflow.HomeController - Welcome home! The client locale is vi. DEBUG: org.springframework.web.servlet.view.JstlView - View name 'home', model {serverTime=05:34:36 ICT 8 tháng 10, 2019} DEBUG: org.springframework.web.servlet.view.JstlView - Forwarding to [/WEB-INF/views/home.jsp] DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7a583b0b DEBUG: org.springframework.web.servlet.DispatcherServlet - Completed 200 OK DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Chain processed normally DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed |
Nếu xem xét kỹ những dòng log trên các bạn sẽ thấy đoạn text “Authorization successful” và class AffirmativeBased chính là class thực hiện việc authorization này!
Class AffirmativeBased implement interface AccessDecisionManager, đây chính là interface chính cho việc authorization trong Spring Security đó các bạn!
Interface AccessDecisionManager có 3 implementation và implementation mà chúng ta thường sử dụng là class AffirmativeBased.
Nếu các bạn xem code của class AffirmativeBased, các bạn sẽ thấy trong phương thức decide() của class này, nó đang loop một list các AccessDecisionVoter để determine xem user có quyền xem resource mà họ đang request hay không?
Đọc thêm code các bạn nhé!