前言:本文会结合相应的代码示例及框架源码。
Spring Security是一个安全框架,侧重于为Java应用程序提供身份验证和受权。 Pivotal 团队的背书,再加上如今主流web开发是围绕着Spring框架的其优点相较于其余安全框架优点不言而喻🙃。废话就很少说了,然咱们开始吧。web
首先导入相关依赖(tip:做者开发使用的框架是SpringBoot)spring
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
接下来是代码api
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; /** * 当前版本是须要配置编码器的 */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { //开启身份认证任何请求,任何请求都须要有基本ROLE_USER身份 //用户还能够进行更细粒度的控制。 http.authorizeRequests() .anyRequest() .authenticated(); //开启表单登陆 http.formLogin(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //使用内存用户信息认证 auth.inMemoryAuthentication().withUser("baozi") //对咱们的明文密码进行加密 .password(passwordEncoder.encode("888")) .roles("USER") .and() .passwordEncoder(passwordEncoder); } }
而后启动咱们的springboot应用,因为我配置的是anyrequest(),第一次请求没有进行过身边验证因此它会跳转到springboot默认生成的登陆页安全
而后输入刚才咱们的用户名("baozi")与密码("888");springboot
简单几步咱们就实现了登陆功能,那其中发生了什么呢?让咱们先看一张流程图(精简版);
其实springsecurity的核心就是一组过滤器。下面列出的三个过滤器是此次demo涉及到的主要过滤器。我将结合部分源码解读。(tip:方法参数及一些不那么重要的代码我省略了)mvc
首先用户请求咱们的受保护资源,第一次用户请求时没有带任何信息的,因此用户的请求会直接到最后一个拦截器FilterSecurityInterceptor进行访问权限处理。框架
// FilterSecurityInterceptor dofilter()中执行invoke() public void invoke(){ ...... // 这里调用父类的方法进行鉴权 InterceptorStatusToken token = super.beforeInvocation(fi); // 这里实际已经调用了咱们的受保护资源 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); ...... } //让咱们展开父类AbstractSecurityInterceptor的beforeInvocation()方法 //tip:该方法内会根据处理结果发布不一样的事件,若是你想作日志的话,能够以此为拓展。 protected InterceptorStatusToken beforeInvocation(){ ...... try{ //决策是否有权限访问咱们的资源,具体在上面SecurityConfig.configure(HttpSecurity http) //中配置决策管理器决策失败抛出异常 this.accessDecisionManager.decide(authenticated, object, attributes); } catch (AccessDeniedException accessDeniedException) { publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; } ...... }
异常逐级抛出,最后由ExceptionTranslationFilter捕获。此过滤器将尝试解析异常SpringSecurityException
异常,解析失败直接抛出,最终由springmvc的异常拦截器捕获处理。解析成功将根据不一样的访问失败缘由返回。ide
public void doFilter(){ try{ chain.doFilter(request, response); }catch(){ //io异常继续抛出 } catch(){ ...... //上面省略代码是尝试将异常转换为SpringSecurityException 成功后将进行。 if(ase != null){ ...... handleSpringSecurityException(request, response, chain, ase); } } //非SpringSecurityException继续抛出 ...... } //此处将根据异常类型或异常信息返回不一样的页面 sendStartAuthentication()负责; private void handleSpringSecurityException(){ //跳转到登陆页。 ex instance AuthenticationException sendStartAuthentication(......) // 分为匿名用户 跳转到登陆页 // 与 非匿名用户(被服务端主动拒绝的用户) accessDeniedHandler执行handle(); //默认的accessDeniedHandler是返回403页面,咱们也能够本身实现accessDeniedHandle接口 //自定义返回 ex instance AuthenticationException sendStartAuthentication(......) or accessDeniedHandle.handle() }
而后咱们被引导到了登陆页(本次不考虑非匿名用户被拒绝的状况),此次咱们登陆将带上username password;
还有请求的是/login post方法(tip:可自定义),这个请求将被AbstractAuthenticationProcessingFilter::UsernamePasswordAuthenticationFilter过滤器进行处理。spring-boot
public void doFilter(){ ...... //抽象方法交由子类实现,本例为UsernamePasswordAuthenticationFilter //内部进行相应的用户验证,成功则将Authentication加入安全上下文。 authResult = attemptAuthentication(request, response); ...... //处理失败跳转到失败的页面,可自定义 unsuccessfulAuthentication(request, response, failed); ...... //处理成功转发到原请求handler,可自定义 successfulAuthentication(request, response, chain, authResult); } }
到此登陆处理流程就完成了。post
总结一下
springsecurity将简单易用的api暴露给咱们,但的"复杂"的流程封装在其中.可能第一次接触的开发者有点懵,可是归根结底只是一组过滤器,弄明白顺序,这也是往后咱们"把玩"这个框架的最大前提。下面我列出了主要的过滤器及顺序,从上到下依次执行。
ChannelProcessingFilter ConcurrentSessionFilter SecurityContextPersistenceFilter LogoutFilter X509AuthenticationFilter AbstractPreAuthenticatedProcessingFilter CasAuthenticationFilter UsernamePasswordAuthenticationFilter ConcurrentSessionFilter OpenIDAuthenticationFilter org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter ConcurrentSessionFilter DigestAuthenticationFilter org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter BasicAuthenticationFilter RequestCacheAwareFilter SecurityContextHolderAwareRequestFilter JaasApiIntegrationFilter RememberMeAuthenticationFilter AnonymousAuthenticationFilter SessionManagementFilter ExceptionTranslationFilter FilterSecurityInterceptor SwitchUserFilter
根据不一样的状况加载的过滤器也有所不一样,具体可观察控制台,会有相应的日志报告。
下节我将主要讲解与分析被我一笔带过的AbstractAuthenticationProcessingFilter,好了今天的分享就到这里:-)