文章部分圖片來自參考資料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 .