日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Web Application Security

發布時間:2025/5/22 javascript 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Web Application Security 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么80%的碼農都做不了架構師?>>> ??

基本流程

Spring Security認證過程的發起

(引用 http://blog.csdn.net/kaikai8552/article/details/3932370)

發起的條件:
????? 用戶訪問資源時,發生認證異常(AuthenticationException)或授權異常(AccessDeniedException),ExceptionTranslationFilter通過調用AuthenticationEntryPoint的commence方法發起認證過程。如果ExceptionTranslationFilter接收到的是授權異常,并且當前認證過的票據不是匿名票據(AnonymousAuthenticationToken),將不會發起認證過程,而是交給AccessDeniedHandler處理(一般會直接提示用戶拒絕訪問)。

發起過程:
????? 發起認證之前,Spring Security會將用戶之前的請求信息保存在session里。還會清空SecurityContextHolder中的票據。AuthenticationEntryPoint將用戶定向到認證的入口,收集認證信息。認證入口一般是個頁面,需要用戶輸入用戶名和密碼,也有其他方式的入口。收集到認證信息之后,重新提交認證請求。認證信息會再次通過過濾器,由AuthenticationManager認證。AuthenticationEntryPoint是用戶提供憑證的入口,真正的認證是由過濾器來完成。(但是DigestProcessingFilter的實現,是直接使用了一個userDetailsService,這是個特例并且這個過濾器也沒有繼承SpringSecurityFilter)

下面是Spring Security提供的幾個AuthenticationEntryPoint的實現。

AuthenticationProcessingFilterEntryPoint
????? 會生成一個用于認證的表單,收集認證信息。生成的頁面的URL缺省值是/j_spring_security_check,可以配置自己指定的URL。只有以這個URL結尾的請求收集上來的認證信息,才會被AuthenticationProcessingFilter用來認證。用戶提交的用戶名和密碼之后轉交AuthenticationManager處理認證。

BasicProcessingFilterEntryPoint
????? 將會向瀏覽器發送一個RFC2616規定的basic認證頭信息。瀏覽器在識別到認證頭之后,會彈出對話框,收集用戶名和密碼。瀏覽器會將收集到的用戶名和密碼,打包成RFC標準的認證響應,發送到服務器。在下一次請求到達BasicProcessingFilter時,過濾器會提取認證信息,并由AuthenticationManager處理認證。

DigestProcessingFilterEntryPoint
????? 和Basic認證類似,會向瀏覽器發送一個RFC2616標準的digest認證頭信息。瀏覽器在收到認證頭后,會彈出對話框,
收集用戶名和密碼。瀏覽器會將收集到的用戶名和密碼,打包成RFC標準的認證響應,發送到服務器。在下一次請求到達DigestProcessingFilter時,過濾器會提取認證信息,并由AuthenticationManager處理認證。

PreAuthenticatedProcessingFilterEntryPoint
????? 簡單的向瀏覽器發送一個錯誤頁面,不做任何處理。

FilterChain Filter Order

The order that filters are defined in the chain is very important. Irrespective of which filters you are actually using, the order should be as follows:

  • ChannelProcessingFilter, because it might need to redirect to a different protocol

  • ConcurrentSessionFilter, because it doesn't use any SecurityContextHolder functionality but needs to update the SessionRegistry to reflect ongoing requests from the principal。檢查Session超時,更新最近訪問信息。

  • SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and any changes to the SecurityContext can be copied to the HttpSession when the web request ends (ready for use with the next web request)。從Session中加載認證信息,請求完成后,保存認證信息。

  • Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilter etc - so that theSecurityContextHolder can be modified to contain a valid Authentication request token

  • The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your servlet container

  • RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, and the request presents a cookie that enables remember-me services to take place, a suitable remembered Authentication object will be put there

  • AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, an anonymousAuthentication object will be put there

  • ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an appropriateAuthenticationEntryPoint can be launched

  • FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied

  • 順序定義如下:

    enum SecurityFilters {FIRST (Integer.MIN_VALUE),CHANNEL_FILTER,CONCURRENT_SESSION_FILTER,SECURITY_CONTEXT_FILTER,LOGOUT_FILTER,X509_FILTER,PRE_AUTH_FILTER,CAS_FILTER,FORM_LOGIN_FILTER,OPENID_FILTER,LOGIN_PAGE_FILTER,DIGEST_AUTH_FILTER,BASIC_AUTH_FILTER,REQUEST_CACHE_FILTER,SERVLET_API_SUPPORT_FILTER,JAAS_API_SUPPORT_FILTER,REMEMBER_ME_FILTER,ANONYMOUS_FILTER,SESSION_MANAGEMENT_FILTER,EXCEPTION_TRANSLATION_FILTER,FILTER_SECURITY_INTERCEPTOR,SWITCH_USER_FILTER,LAST (Integer.MAX_VALUE);private static final int INTERVAL = 100;private final int order;private SecurityFilters() {order = ordinal() * INTERVAL;}private SecurityFilters(int order) {this.order = order;}public int getOrder() {return order;} }

    一、The Security Namespace

    1、<http … />

    (1) 如果聲明了該元素,則會檢查是否創建了FilterChainProxy,如果沒有,則創建name為”springSecurityFilterChain”的FilterChainProxy Bean,并初始化FilterChainProxy.filterChains屬性。

    (2) 對于每個http元素,會創建SecurityFilterChain對象。

    a. 并會依次使用HttpConfigurationBuilder、AuthenticationConfigBuilder創建相關的Filter,如果子元素有custom-filter,則創建自定義Filter,將所有的filter添加到SecurityFilterChain對象中。

    HttpConfigurationBuilder:

    創建FilterSecurityInterceptor Bean。多個<interceptor-url …/>會為每個元素會注冊一個Map.Entry<RequestMatcher,SecurityConfig>。FilterSecurityInterceptor 默認的access-decision-manager-ref 為AffirmativeBased實例,包含RoleVoter,AuthenticatedVoter兩個voter

    b. 為SecurityFilterChain創建filterChainMatcher:

    String requestMatcherRef = element.getAttribute(ATT_REQUEST_MATCHER_REF);String filterChainPattern = element.getAttribute(ATT_PATH_PATTERN);if (StringUtils.hasText(requestMatcherRef)) {if (StringUtils.hasText(filterChainPattern)) {pc.getReaderContext().error("You can't define a pattern and a request-matcher-ref for the " +"same filter chain", pc.extractSource(element));}filterChainMatcher = new RuntimeBeanReference(requestMatcherRef);} else if (StringUtils.hasText(filterChainPattern)) {filterChainMatcher = MatcherType.fromElement(element).createMatcher(filterChainPattern, null);} else {filterChainMatcher = new RootBeanDefinition(AnyRequestMatcher.class);}

    (3) 默認情況下filter情況

    a. ChannelProcessingFilter (CHANNEL_FILTER) ,根據intercept-url的requires-channel屬性,來建立matcher與channel關聯。如果有配置,則創建filter,否則不創建

    b. ConcurrentSessionFilter( CONCURRENT_SESSION_FILTER),http元素配置了session-management子元素時,且session-management配置了concurrency-control子元素時創建該filter

    c. SecurityContextPersistenceFilter (SECURITY_CONTEXT_FILTER),如果sessionPolicy為stateless,則使用 NullSecurityContextRepository;否則使用HttpSessionSecurityContextRepository存儲認證信息。默認該filter是配置的

    d. LogoutFilter (LOGOUT_FILTER),如果配置了子元素logout或者http設置了autoConfig為true,會創建該filter。當用戶訪問logout url(如果不配置默認為/j_spring_security_logout)時,分別調用SecurityContextLogoutHandler、rememberMeServices(http子元素配置的remember-me元素)、CookieClearingLogoutHandler(配置了delete-cookie屬性)

    e.J2eePreAuthenticatedProcessingFilter (PRE_AUTH_FILTER)。如果http配置了子元素jee

    f. UsernamePasswordAuthenticationFilter (FORM_LOGIN_FILTER)。配置了form-login子元素或者authConfig為true時創建。默認登錄處理的URL為/j_spring_security_check。如果請求的URI為/j_spring_security_check,則調用authenticationManager進行認證。

    g. DefaultLoginPageGeneratingFilter (LOGIN_PAGE_FILTER),如果http配置了form-login,但form-login沒有提供login-page屬性,則會輸出spring實現的登陸頁面。

    h. BasicAuthenticationFilter(BASIC_AUTH_FILTER),如果http配置了http-basic子元素或者autoConfig為true。從http header中讀取認證信息,如果SecurityContextHolder已經包含認證完成的Authentication,則不需要進行認證。否則調用authenticationManager進行認證。

    i. SecurityContextHolderAwareRequestFilter(SERVLET_API_FILTER)。如果配置了servlet-api-provision為false,則不會創建。否則創建該filter。該filter會使用SecurityContextHolderAwareRequestWrapper封裝request。

    j. RememberMeAuthenticationFilter (REMEMBER_ME_FILTER)。如果配置了dataSource,則使用PersistentTokenBasedRememberMeServices,否則使用TokenBasedRememberMeServices。PersistentTokenBasedRememberMeServices從數據庫中,讀取remember me的認證信息,AccountStatusUserDetailsChecker檢查賬戶狀態;調用authenticationManager進行認證,認證通過后,保存到SecurityContextHolder中。

    k. AnonymousAuthenticationFilter (ANONYMOUS_FILTER)。 如果http配置了anonymous 子元素且enable為false,則不會創建該元素。如果SecurityContextHolder中沒有Authentication對象,則創建AnonymousAuthenticationToken,保存到SecurityContextHolder中。

    l. SessionManagementFilter (SESSION_MANAGEMENT_FILTER) 。 http元素配置了session-management子元素時。且session-management配置了concurrency-control子元素;或session-fixation-protection(默認為migrateSession)不為none;或配置了invalid-session-url;或配置了session-authentication-strategy-ref。concurrency-control使用ConcurrentSessionControlStrategy處理認證成功后的操作,除了SessionFixationProtectionStrategy的功能,另外會注冊新生成的session到sessionRegister中。其他使用SessionFixationProtectionStrategy,每次創建新的session,復制數據,用于更改sessionId,這樣保護sessionId被惡意使用:

    /*** Called when a user is newly authenticated.* <p>* If a session already exists, and matches the session Id from the client, a new session will be created, and the* session attributes copied to it (if {@code migrateSessionAttributes} is set).* If the client's requested session Id is invalid, nothing will be done, since there is no need to change the* session Id if it doesn't match the current session.* <p>* If there is no session, no action is taken unless the {@code alwaysCreateSession} property is set, in which* case a session will be created if one doesn't already exist.*/public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {boolean hadSessionAlready = request.getSession(false) != null;if (!hadSessionAlready && !alwaysCreateSession) {// Session fixation isn't a problem if there's no sessionreturn;}// Create new session if necessaryHttpSession session = request.getSession();if (hadSessionAlready && request.isRequestedSessionIdValid()) {// We need to migrate to a new sessionString originalSessionId = session.getId();if (logger.isDebugEnabled()) {logger.debug("Invalidating session with Id '" + originalSessionId +"' " + (migrateSessionAttributes ?"and" : "without") + " migrating attributes.");}Map<String, Object> attributesToMigrate = extractAttributes(session);session.invalidate();session = request.getSession(true); // we now have a new sessionif (logger.isDebugEnabled()) {logger.debug("Started new session: " + session.getId());}if (originalSessionId.equals(session.getId())) {logger.warn("Your servlet container did not change the session ID when a new session was created. You will" +" not be adequately protected against session-fixation attacks");}transferAttributes(attributesToMigrate, session);onSessionChange(originalSessionId, session, authentication);}}

    m. ExceptionTranslationFilter (EXCEPTION_TRANSLATION_FILTER),從exception cause chain中,提取AuthenticationException或AccessDeniedException。

    n. FilterSecurityInterceptor? (FILTER_INTECEPTOR_FILTER) 。最重要的filter,用戶的認證就是改filter實現的。進行認證與權限檢查:

    Authentication authenticated = authenticateIfRequired();// Attempt authorizationtry {this.accessDecisionManager.decide(authenticated, object, attributes);}catch (AccessDeniedException accessDeniedException) {publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));throw accessDeniedException;}

    2、authentication-manager

    默認會創建ProviderManager Bean,包含了配置的AuthenticationProvider的Bean實例

    3、authentication-provider

    默認創建DaoAuthenticationProvider bean

    DaoAuthenticationProvider中,additionalAuthenticationChecks包含了對密碼的校驗

    protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {Object salt = null;if (this.saltSource != null) {salt = this.saltSource.getSalt(userDetails);}if (authentication.getCredentials() == null) {logger.debug("Authentication failed: no credentials provided");throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);}String presentedPassword = authentication.getCredentials().toString();if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {logger.debug("Authentication failed: password does not match stored value");throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);}}

    ?

    二、Spring Security OAuth2

    Spring Security OAuth2 包含如下標簽:

    registerBeanDefinitionParser("authorization-server", new AuthorizationServerBeanDefinitionParser()); registerBeanDefinitionParser("resource-server", new ResourceServerBeanDefinitionParser()); registerBeanDefinitionParser("client-details-service", new ClientDetailsServiceBeanDefinitionParser()); registerBeanDefinitionParser("client", new ClientBeanDefinitionParser()); registerBeanDefinitionParser("resource", new ResourceBeanDefinitionParser()); registerBeanDefinitionParser("rest-template", new RestTemplateBeanDefinitionParser()); registerBeanDefinitionParser("expression-handler", new ExpressionHandlerBeanDefinitionParser()); registerBeanDefinitionParser("web-expression-handler", new WebExpressionHandlerBeanDefinitionParser());
    Spring Security OAuth2流程

    (1) 在Spring 配置文件中,為 API資源添加http元素,該元素需要添加resourceServerFilter,同時必須設置create-session="never":

    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />

    resourceServerFilter添加了OAuth2AuthenticationProcessingFilter,并使用OAuth2AuthenticationManager進行認證過程,如果resourceServerFilter不指定entry-point-ref屬性,則默認使用OAuth2AuthenticationEntryPoint處理結果

    OAuth2AuthenticationProcessingFilter負責從request中提取token信息,生成PreAuthenticatedAuthenticationToken,調用OAuth2AuthenticationManager.authentication進行校驗。

    (2) Client訪問資源時,resourceServer會進行OAuth 2.0 規范中要求的認證。如果token為空,則調用接下來的filter,不會在SecurityContextHolder中添加任何Authentication。如果使用token沒有查詢到對應的OAuth2Authentication,則拋出InvalidTokenException;接著調用tokenService中的loadAuthentication(token),并檢查resourceId是否在允許的resourceIds中。

    client與user之間的授權關聯是使用OAuth2Authentication表示的,其中分別包含了clientAuthentication(AuthorizationRequest)與userAuthentication(Authentication)。是通過指定的resource-server中配置的authentication-manager進行校驗的。

    (3) 如果沒有關聯的accessToken,不會生成任何Authentication對象,則在FilterSecurityInterceptor 中,則會拋出AuthenticationCredentialsNotFoundException,通過配置的entry-point-ref,返回錯誤信息。

    (4) Client收到錯誤信息后,會請求/oauth/authorize,獲取authorization code。獲取成功后,繼續請求/oauth/token獲取accessToken

    ?

    (5) 訪問/oauth/token時,需要驗證client,使用ClientCredentialsTokenEndpointFilter。在經過過濾器后,會到處理的Tokenpoint。

    a.ClientCredentialsTokenEndpointFilter什么作用?

    校驗client_id與client_secret,并生成UsernamePasswordAuthenticationToken,并調用SecurityContextHolder.getContext().setAuthentication(authResult);保存認證結果

    b. 哪兒校驗grant code?

    在AuthorizationCodeTokenGranter類中:

    @Overrideprotected OAuth2Authentication getOAuth2Authentication(AuthorizationRequest authorizationRequest) {Map<String, String> parameters = authorizationRequest.getAuthorizationParameters();String authorizationCode = parameters.get("code");String redirectUri = parameters.get("redirect_uri");if (authorizationCode == null) {throw new OAuth2Exception("An authorization code must be supplied.");}AuthorizationRequestHolder storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);if (storedAuth == null) {throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);}AuthorizationRequest pendingAuthorizationRequest = storedAuth.getAuthenticationRequest();if (pendingAuthorizationRequest.getRedirectUri() != null&& !pendingAuthorizationRequest.getRedirectUri().equals(redirectUri)) {throw new RedirectMismatchException("Redirect URI mismatch.");}String pendingClientId = pendingAuthorizationRequest.getClientId();String clientId = authorizationRequest.getClientId();if (clientId != null && !clientId.equals(pendingClientId)) {// just a sanity check.throw new InvalidClientException("Client ID mismatch");}// Secret is not required in the authorization request, so it won't be available// in the pendingAuthorizationRequest. We do want to check that a secret is provided// in the token request, but that happens elsewhere.Map<String, String> combinedParameters = new HashMap<String, String>(storedAuth.getAuthenticationRequest().getAuthorizationParameters());// Combine the parameters adding the new ones last so they override if there are any clashescombinedParameters.putAll(parameters);// Similarly scopes are not required in the token request, so we don't make a comparison here, just// enforce validity through the AuthorizationRequestFactory.DefaultAuthorizationRequest outgoingRequest = new DefaultAuthorizationRequest(pendingAuthorizationRequest);outgoingRequest.setAuthorizationParameters(combinedParameters);Authentication userAuth = storedAuth.getUserAuthentication();return new OAuth2Authentication(outgoingRequest, userAuth);}

    ?

    ?

    1、authorization-server

    該元素負責創建AuthorizationEndpoint與TokenEndpoint

    ?

    首先設置authorizationEndpointUrl與tokenEndpointUrl,如果設置了這兩個屬性,則注冊EndpointValidationFilter Bean,該Bean會分別將設置的兩個URL重定向到AuthorizationEndpoint與TokenEndpoint。如果指定了這兩個屬性,則需要在web.xml中配置名稱為oauth2EndpointUrlFilter的DelegatingFilterProxy Filter

    AuthorizationEndpoint

    該類聲明如下

    @FrameworkEndpoint @SessionAttributes("authorizationRequest") @RequestMapping(value = "/oauth/authorize") public class AuthorizationEndpoint extends AbstractEndpoint implements InitializingBean {

    服務于/oauth/authorize

    默認的TokenGranter為CompositeTokenGranter以下的每個元素會為AuthorizationEndpoint添加一種類型的TokenGranter

    <oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices"user-approval-handler-ref="userApprovalHandler"><oauth:authorization-code /> <!--AuthorizationCodeTokenGranter 可以指定的屬性有:disabled: Boolean value specifying whether the authorization code mechanism is disabled. This effectively disables the authorization code grant mechanism.services-ref: The reference to the bean that defines the authorization code services (instance of org.springframework.security.oauth2.provider.code.AuthorizationCodeServices)user-approval-page: The URL of the page that handles the user approval form.approval-parameter-name: The name of the form parameter that is used to indicate user approval of the client authentication request.--><oauth:implicit /><oauth:refresh-token /><oauth:client-credentials /><oauth:password /></oauth:authorization-server>

    ?

    ?

    2、resource-server

    定義OAuth2AuthenticationProcessingFilter,使用指定的authentication-manager進行校驗??梢灾付╮esource-id屬性,用來標識資源ID,對資源進行分類。在用戶授權時,會記錄資源ID,標識此次授權可以訪問那些資源。

    ?

    ?

    ----------------------------------------------------------------

    學習部分:

    1、ListBeanFactory

    使用BeanFactory機制,把bean作為List注入其他Bean

    BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS));

    2、HandlerMapping

    在DispatcherServlet中,initHandlerMappings會讀取applicationContext中定義的所有的HandlerMapping及其子類型的Bean,注冊進來,從而可以根據request獲得HandlerMapping。這樣就可以任意擴展HandlerMapping,OAuth機制就擴展了HandlerMapping (FrameworkEndpointHandlerMapping),通過@FrameworkEndpoint來標識Bean為Handler

    轉載于:https://my.oschina.net/guoxf1/blog/73121

    總結

    以上是生活随笔為你收集整理的Spring Web Application Security的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。