天天看点

(一)Spring Security 简单原理Spring Security 原理:

Spring Security 原理:

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中,以备后面访问资源时使用。

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;
            }