Spring Security原理

Spring Security原理是通过使用过滤器Filter,实现责任链设计模式,通过预先configure(HttpSecurity http)设定责任链过滤规则实现认证和授权。
Spring Security原理_第1张图片

过滤器通过spring容器托管实现,需要使用代理DelegatingFilterProxy实现
Spring Security原理_第2张图片
FilterChainProxy 过滤器链代理,是通过DelegatingFilterProxy代理Spring容器中的对象。

Spring Security原理_第3张图片
Spring Security原理_第4张图片
Spring Security原理_第5张图片
官方文档中描述Filter列表包括
https://docs.spring.io/spring-security/site/docs/5.3.3.BUILD-SNAPSHOT/reference/html5/#modules

ChannelProcessingFilter

ConcurrentSessionFilter

WebAsyncManagerIntegrationFilter

SecurityContextPersistenceFilter

HeaderWriterFilter

CorsFilter

CsrfFilter

LogoutFilter

OAuth2AuthorizationRequestRedirectFilter

Saml2WebSsoAuthenticationRequestFilter

X509AuthenticationFilter

AbstractPreAuthenticatedProcessingFilter

CasAuthenticationFilter

OAuth2LoginAuthenticationFilter

Saml2WebSsoAuthenticationFilter

UsernamePasswordAuthenticationFilter

ConcurrentSessionFilter

OpenIDAuthenticationFilter

DefaultLoginPageGeneratingFilter

DefaultLogoutPageGeneratingFilter

DigestAuthenticationFilter

BearerTokenAuthenticationFilter

BasicAuthenticationFilter

RequestCacheAwareFilter

SecurityContextHolderAwareRequestFilter

JaasApiIntegrationFilter

RememberMeAuthenticationFilter

AnonymousAuthenticationFilter

OAuth2AuthorizationCodeGrantFilter

SessionManagementFilter

ExceptionTranslationFilter

FilterSecurityInterceptor

SwitchUserFilter

这些Filter列表中的内容与configure(HttpSecurity http)代码配置有关系,我跟踪的Filter包括如下,使用的Form登录

[
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1015a4b9, 
org.springframework.security.web.access.ExceptionTranslationFilter@6d08b4e6, 
org.springframework.security.web.header.HeaderWriterFilter@7bf01cb,
org.springframework.security.web.session.SessionManagementFilter@12b5736c, 
org.springframework.security.web.context.SecurityContextPersistenceFilter@2cd4e16a, 
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7b477141, 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3625a016,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4364863, 
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@267cde2e, 
org.springframework.security.web.authentication.logout.LogoutFilter@1f1cddf3,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@46039a21
]

Configure方法代码

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/org/**")
        .hasRole("ORG")
        
        .antMatchers("/user/**")
        .hasRole("USER")
               
        .antMatchers("/orguser/**")
        .access("hasRole('ORG') and hasRole('USER')")
               
        .antMatchers("/role/**")
        .access("hasAnyRole('ORG') and  hasRole('ROLE')")

        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        .loginProcessingUrl("/logindone")
        .successHandler(new AuthenticationSuccessHandler() {            //登陆成功后
            @Override
            public void onAuthenticationSuccess(HttpServletRequest req,
                                                HttpServletResponse resp,
                                                Authentication auth)    //当前用户登陆信息
                    throws IOException {
                Object principal = auth.getPrincipal();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                resp.setStatus(200);
                Map<String, Object> map = new HashMap<>();
                map.put("status", 200);
                map.put("msg", principal);
                ObjectMapper om = new ObjectMapper();
                out.write(om.writeValueAsString(map));
                out.flush();
                out.close();
            }
        })
        .failureHandler(new AuthenticationFailureHandler() {         //登陆失败后
            @Override
            public void onAuthenticationFailure(HttpServletRequest req,
                                                HttpServletResponse resp,
                                                AuthenticationException e)  //获取登陆失败原因
                    throws IOException {
            	e.printStackTrace();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                resp.setStatus(401);
                Map<String, Object> map = new HashMap<>();
                map.put("status", 401);
                if (e instanceof LockedException) {
                    map.put("msg", "账户被锁定,登录失败!");
                } else if (e instanceof BadCredentialsException) {
                    map.put("msg", "账户名或密码输入错误,登录失败!");
                } else if (e instanceof DisabledException) {
                    map.put("msg", "账户被禁用,登录失败!");
                } else if (e instanceof AccountExpiredException) {
                    map.put("msg", "账户已过期,登录失败!");
                } else if (e instanceof CredentialsExpiredException) {
                    map.put("msg", "密码已过期,登录失败!");
                } else {
                    map.put("msg", "登录失败!");
                }
                ObjectMapper om = new ObjectMapper();
                out.write(om.writeValueAsString(map));
                out.flush();
                out.close();
            }
        })
        .permitAll()
        .and()
        .logout()//开启注销登陆
        .logoutUrl("/logout")//注销登陆请求url
        .clearAuthentication(true)//清除身份信息
        .invalidateHttpSession(true)//session失效
        .addLogoutHandler(new LogoutHandler() {//注销处理
            @Override
            public void logout(HttpServletRequest req,
                               HttpServletResponse resp,
                               Authentication auth) {

            }
        })
        .logoutSuccessHandler(new LogoutSuccessHandler() {     //注销成功处理
            @Override
            public void onLogoutSuccess(HttpServletRequest req,
                                        HttpServletResponse resp,
                                        Authentication auth)
                    throws IOException {
                resp.sendRedirect("/login_page");              //跳转到自定义登陆页面
            }
        })
        .and()
        .csrf()
        .disable();

    }

通过代码跟踪发现Form表单登录,后台使用会话记录登录状态,这些都可以配置,Spring Security提供了灵活配置方式。
Spring Security原理_第6张图片
Spring Security原理_第7张图片
登录成功后出现JSESSIONID cookie,如果将其删除,必须重新登录,跟踪其后台代码发现
package org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
Spring Security原理_第8张图片
初始化了会话关联Filter
package org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer

Spring Security原理_第9张图片
package org.springframework.security.web.session.SessionManagementFilter
Spring Security原理_第10张图片
package org.springframework.security.web.authentication.session.AbstractSessionFixationProtectionStrategy
Spring Security原理_第11张图片

判断每次请求是否已经登录是在这个Filter中实现
package org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter
Spring Security原理_第12张图片

你可能感兴趣的:(SpringBoot)