(一)Spring Security 簡單原理

Spring Security 原理:

Spring Security框架中有很多攔截器,這裏就不一一贅述,後面文章會詳細描述,現在來看看最核心的幾個。
首先來看看Spring Security 框架工作流程圖
這裏寫圖片描述
Spring Security 核心流程分爲用戶認證和授權管理兩部分。
用戶認證
客戶端發出登錄請求,被AuthenticationProcessingFilter攔截器攔截,先判斷訪問的url是否需要權限驗證,若不需要,則放行,繼續調用下一個攔截器,若需要權限,則調用AuthenticationManager的實現

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);

            return;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Request is to process authentication");
        }

        Authentication authResult;

        try {
            authResult = attemptAuthentication(request, response);
            if (authResult == null) {
                // return immediately as subclass has indicated that it hasn't completed
                // authentication
                return;
            }
            sessionStrategy.onAuthentication(authResult, request, response);
        }
        catch (InternalAuthenticationServiceException failed) {
            logger.error(
                    "An internal error occurred while trying to authenticate the user.",
                    failed);
            unsuccessfulAuthentication(request, response, failed);

            return;
        }
        catch (AuthenticationException failed) {
            // Authentication failed
            unsuccessfulAuthentication(request, response, failed);

            return;
        }

        // Authentication success
        if (continueChainBeforeSuccessfulAuthentication) {
            chain.doFilter(request, response);
        }

        successfulAuthentication(request, response, chain, authResult);
    }

public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

AuthenticationManager調用ProviderManager來獲取用戶的認證信息,ProviderManager存在多種不同的provider,因爲用戶的認證信息可以在xml配置文件上,可以在LADP服務器上,可以在數據庫上。
驗證通過後,將用戶的認證信息封裝成UserDetails對象放在全局緩存SecuriyContextHolder中,以備後面訪問資源時使用。

UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
protected void successfulAuthentication(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain, Authentication authResult)
            throws IOException, ServletException {

        if (logger.isDebugEnabled()) {
            logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
                    + authResult);
        }

        SecurityContextHolder.getContext().setAuthentication(authResult);

        rememberMeServices.loginSuccess(request, response, authResult);

        // Fire event
        if (this.eventPublisher != null) {
            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
                    authResult, this.getClass()));
        }

        successHandler.onAuthenticationSuccess(request, response, authResult);
    }

授權管理
訪問url時,會通過AbstractSecurityInterceptor攔截器攔截,這時會調用FilterInvocationSecurityMetadataSource的方法來獲取該url所需要的全部的權限

Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                .getAttributes(object);

再調用授權管理器AccessDecisionManager,並傳遞權限列表

this.accessDecisionManager.decide(authenticated, object, attributes);

AccessDecisionManager到全局緩存SecurityContextHolder中拿到用戶認證信息

Authentication authentication = SecurityContextHolder.getContext()
                .getAuthentication();

再根據AccessDecisionManager的授權對策(一票決定、一票否定、少數服從多數)進行判斷,如果權限足夠,則返回,否則拋出權限不足異常,調用權限不足頁面。

try {
                returnedObject = afterInvocationManager.decide(token.getSecurityContext()
                        .getAuthentication(), token.getSecureObject(), token
                        .getAttributes(), returnedObject);
            }
            catch (AccessDeniedException accessDeniedException) {
                AuthorizationFailureEvent event = new AuthorizationFailureEvent(
                        token.getSecureObject(), token.getAttributes(), token
                                .getSecurityContext().getAuthentication(),
                        accessDeniedException);
                publishEvent(event);

                throw accessDeniedException;
            }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章