文章部分图片来自参考资料html
问题 :web
Spring Security 是个安全框架,能够提供认证,防止网络功能等功能,能够结合 spring-security-oauth 框架一块儿使用。本文主要讲的是几个重要的类结构,还有工做原理,工做流程会在下一篇介绍。spring
Application security boils down to two more or less independent problems: authentication (who are you?) and authorization (what are you allowed to do?).数组
应用安全关注两个问题 : authentication (认证,你是谁)和 authorization (受权,你能够作什么)。安全
认证的目的是证实你是谁的问题,在生活中,咱们证实本身的身份有多种方式:身份证证实,指纹证实等等,便是说认证的方式有多种,ss框架中认证的方式定义为 provider , 管理这些认证方式的是 providerManager ,下面咱们看一下这两个类的源码(源码不完整,只为了展现内部做用) :cookie
public interface AuthenticationProvider { Authentication authenticate(Authentication authentication) throws AuthenticationException; boolean supports(Class<?> authentication); } public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean { private List<AuthenticationProvider> providers = Collections.emptyList(); public Authentication authenticate(Authentication authentication) ... for (AuthenticationProvider provider : getProviders()) { ... try { result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException e) { prepareException(e, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw e; } catch (InternalAuthenticationServiceException e) { prepareException(e, authentication); throw e; } catch (AuthenticationException e) { lastException = e; } } .... eventPublisher.publishAuthenticationSuccess(result); return result; // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(messages.getMessage( "ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } prepareException(lastException, authentication); throw lastException; } }
它们的关系能够用这张图 :网络
能够看到 ProviderManager 内部放着一个 AuthenticationProvider (认证方式)的数组,当要认证的是否,逐个遍历调用认证的方法。而 ProviderManager 继承一个 AuthenticationManager ,上面的authenticate()方法正是来自 AuthenticationManager 。app
public interface AuthenticationManager { Authentication authenticate(Authentication authentication) throws AuthenticationException; }
这个认证方法能够作三件事 : 框架
return an
Authentication
(normally withauthenticated=true
) if it can verify that the input represents a valid principal.lessthrow an
AuthenticationException
if it believes that the input represents an invalid principal.return
null
if it can’t decide.
这样咱们就能够知道认证一切核心认证的操做实际必须由 AuthenticationManager 来完成,ss提供了一个类AuthenticationManagerBuilder 来让咱们方便地配置AuthenticationManager (例如我想用怎么样的认证方式,哪一个节点不须要认证, 哪一个节点须要等等),这个类就像咱们平时的 helper 类同样。例如像下面这样使用 :
@Configuration public class ApplicationSecurity extends WebSecurityConfigurerAdapter { @Autowired DataSource dataSource; ... // web stuff here @Override public configure(AuthenticationManagerBuilder builder) { builder.jdbcAuthentication().dataSource(dataSource).withUser("dave") .password("secret").roles("USER"); } }
继承 WebSecurityConfigurerAdapter ,重写 configure 方法,而后配置 AuthenticationManagerBuilder 。
和 AuthenticationManager(的实现类) 持有一个 privoder 列表同样,AccessDecisionManager (的实现类)持有 AccessDecisionVoter 列表 , DecisionVoter 是从名字就知道是判断受权的策略。 例如 AccessDecisionManager 的一个实现类,受权的过程
public class AffirmativeBased extends AbstractAccessDecisionManager { public AffirmativeBased(List<AccessDecisionVoter<? extends Object>> decisionVoters) { super(decisionVoters); } public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException { int deny = 0; for (AccessDecisionVoter voter : getDecisionVoters()) { int result = voter.vote(authentication, object, configAttributes); if (logger.isDebugEnabled()) { logger.debug("Voter: " + voter + ", returned: " + result); } switch (result) { case AccessDecisionVoter.ACCESS_GRANTED: return; case AccessDecisionVoter.ACCESS_DENIED: deny++; break; default: break; } } if (deny > 0) { throw new AccessDeniedException(messages.getMessage( "AbstractAccessDecisionManager.accessDenied", "Access is denied")); } // To get this far, every AccessDecisionVoter abstained checkAllowIfAllAbstainDecisions(); } }
Spring Security in the web tier (for UIs and HTTP back ends) is based on Servlet
Filters
SS在http后台中起做用主要是基于 Servlet Filters 的,咱们先来看看什么是 Filter 是如何做用在 Servlet 中的。
能够看到不一样的过滤器做用在 Servlet 以前,多个造成的就是一条过滤器链( Filters Chain ),每一个Filter 有个 Order 顺序,能够经过 @Order 来设置Filter 的 Order ,设置先后顺序。SS自己也是一个 Filter ,使用一个代理,委托了一个 Filter Chain ,以下图 :
In fact there is even one more layer of indirection in the security filter: it is usually installed in the container as a
DelegatingFilterProxy
, which does not have to be a Spring@Bean
. The proxy delegates to aFilterChainProxy
which is always a@Bean
, usually with a fixed name ofspringSecurityFilterChain
. It is theFilterChainProxy
which contains all the security logic arranged internally as a chain (or chains) of filters. All the filters have the same API (they all implement theFilter
interface from the Servlet Spec) and they all have the opportunity to veto the rest of the chain.
springSecurityFilterChain 是个接口,DefaultSecurityFilterChain 是它的实现类,而DefaultSecurityFilterChain 内部存在这一个 Filters 列表,关于SS中的过滤器和他们的执行顺序(Order)能够查看 官方文档,当咱们须要自定义Filter的时候就会用到。 当请求到来时,在 ss 里边的 Filter就会做用请求,以下图 :
SS自己有个 Filter Chain ,咱们新建立的 Filter Chain 的 Order 设置高点,关于为何会有自定义 Filter Chain 这样的场景咱们能够看官方文档的举得例子。
Many applications have completely different access rules for one set of resources compared to another. For example an application that hosts a UI and a backing API might support cookie-based authentication with a redirect to a login page for the UI parts, and token-based authentication with a 401 response to unauthenticated requests for the API parts. Each set of resources has its own
WebSecurityConfigurerAdapter
with a unique order and a its own request matcher. If the matching rules overlap the earliest ordered filter chain will win.
@Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER - 10) public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/foo/**") ...; } }
A security filter chain (or equivalently a
WebSecurityConfigurerAdapter
) has a request matcher that is used for deciding whether to apply it to an HTTP request. Once the decision is made to apply a particular filter chain, no others are applied. But within a filter chain you can have more fine grained control of authorization by setting additional matchers in theHttpSecurity
configurer.
@Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER - 10) public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/foo/**") .authorizeRequests() .antMatchers("/foo/bar").hasRole("BAR") .antMatchers("/foo/spam").hasRole("SPAM") .anyRequest().isAuthenticated(); } }
更多关于SS 的原理看官方的文档 。
介绍了几个SS 中重要的几个类,包括认证和受权,明白了 SS 能够工做的缘由是做为在 Servlet 以前的 Filter .