Spring security学习笔记
创始人
2024-11-12 08:40:40
0

目录

  • 1. 概要
  • 2. spring security原理
    • 2.1 DelegatingFilterProxy
    • 2.2 FilterChainProxy
    • 2.3 SecurityFilterChain
    • 2.4 Spring Security 作用机制
  • 3.Spring Security快速入门
  • 4.高级自定义配置
  • 5. Spring Security 结合 JWT使用

1. 概要

Spring Security是一个用于在Java应用程序中实现 身份验证访问控制 的强大框架。它可以轻松地集成到任何基于Spring的应用程序中,提供了一套丰富的功能来保护应用程序的安全性。

2. spring security原理


对于servlet三大组件,spring security位于Filter过滤链中,SpringSecurity 本质就是一个过滤器链
三大组件

SpringSecurity 采用的是责任链的设计模式,它有一条很长的过滤器链

在这里插入图片描述

2.1 DelegatingFilterProxy

Spring 提供了一个 DelegatingFilterProxy 代理类,DelegatingFilterProxy 实现了Filter,因此它可以被注入到 FilterChain(过滤器链)中,同时,当请求到来时,它会把请求转发到Spring容器 中实现了Filter接口的 Bean 实体,DelegatingFilterProxy会从ApplicationContext中获取FilterBean 实体,然后将请求转发给到它,所以 DelegatingFilterProxy 桥接了 Servlet容器 和 Spring容器。

2.2 FilterChainProxy

DelegatingFilterProxy从Spring容器中获取得到的就是FilterChainProxy实体,而FilterChainProxy也是一个代理类,它最终会将请求转发到 Spring Security 提供的 SecurityFilterChain 中,FilterChainProxy就是 Spring Security 真正的入口起始点

2.3 SecurityFilterChain

SecurityFilterChain作用其实跟Servlet的FilterChain一样,同样维护了很多Filters,这些Filters 是由Spring Security提供的,每个 Security Filter 都有不同的职能,比如登录认证、CSRF防御…

2.4 Spring Security 作用机制

  1. 注册标准Filter:首先,会自动注入一个DelegatingFilterProxy到 Servlet 的FilterChain中。
  2. 请求转发到 Spring Security:当请求到来时,DelegatingFilterProxy就会自动在 Spring容器 中搜索名称为springSecurityFilterChain的Filter实体,其实际类型为FilterChainProxy。DelegatingFilterProxy最终会将请求转发给到FilterChainProxy。
  3. 找到匹配请求处理的SecurityFilterChain:FilterChainProxy内部维护了一系列SecurityFilterChains,他会依据请求内容找到对应处理该请求的SecurityFilterChain。
  4. 请求处理:找到能处理请求的第一个SecurityFilterChain后,就会遍历该SecurityFilterChain内部维护的一系列Filters,依次让这些 Security Filter 处理该请求,完成认证、授权等功能。

3.Spring Security快速入门

  • 步骤1:添加Spring Security依赖
    在你的Spring Boot项目的 pom.xml文件中,添加Spring Security的依赖:

         org.springframework.boot     spring-boot-starter-security  
  • 步骤2:写一个简单地hello接口:
    hello接口

  • 步骤3:浏览器访问接口
    登录界面

    默认的用户名是 user ,默认的登录密码 在每次启动项目时随机生成

    密码

    从项目启动日志中可以看到默认的登录密码,登录成功后,就可以访问hello 接口了。

  • 步骤4:自己配置账户名或者密码
    配置账户

  • 步骤5:创建Spring Security配置类
    创建一个继承自 WebSecurityConfigurerAdapter的配置类,并重写 configure() 方法来配置Spring Security的行为。

    @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {     @Override     protected void configure(HttpSecurity http) throws Exception {         http             .authorizeRequests()  //这开始了一个授权请求的配置链。.authorizeRequests() 方法允许你指定哪些 URL 需要认证和授权。                 .antMatchers("/", "/home").permitAll() // 方法指定了两个 URL 模式,/ 和 /home。.permitAll() 表示这些 URL 可以被任何用户无条件访问,即使他们没有登录。                 .anyRequest().authenticated() //.anyRequest() 表示所有未被前面规则明确指定的请求。.authenticated() 指定这些请求需要经过身份验证才能访问。                                  .and() //.and() 方法用于继续下一个配置步骤。在这里,它用于从授权请求配置切换到其他配置,如表单登录配置。                          .formLogin() //.formLogin() 配置了表单登录功能                 //.loginPage("/login") 指定了登录页面的 URL 注意:配置了.loginPage("/login") ,你必须写这个页面,如果不配置,就用Spring Security 内置的Form登录页面                 .permitAll()   // 表示这个登录页面可以被任何人访问,不需要身份验证。                                  .and()  //同样地,.and() 用于继续到下一个配置步骤,这里是登出配置。              .logout()   //.logout() 开始配置登出功能。                 .logoutUrl("/logout")  //设置了处理登出请求的 URL                 .logoutSuccessUrl("/login") //指定了登出成功后用户会被重定向到的 URL                 .invalidateHttpSession(true) //表示在登出时会销毁 HttpSession。                 .deleteCookies("JSESSIONID");   //用于删除 JSESSIONID cookie,以完全清除用户的会话      } } 

4.高级自定义配置

Spring Security抽象类 WebSecurityConfigAdapter—— 开发者通过继承它就能 得到Spring Security 默认的安全功能。 也可以通过覆盖它提供的方法来自定义自 的安全拦截方案。

  1. WebSecurityConfigAdapter 中默认存在的方法

    /** *在此方法中,可以配置认证管理器(AuthenticationManager),包括定义用户、密码、角色等。例如,可以连接到数据库或LDAP来加载用户信*息,也可以配置内存中的用户数据等等.  */ protected void configure(AuthenticationManagerBuilder auth );   /** *这个方法允许配置不受Spring Security过滤器链保护的资源,比如静态资源、视图解析器资源等。 */ public void configure(WebSecurity web) ;    /** *configure(HttpSecurity http): 开发者通过重写这个方法来配置HTTP安全相关的所有细节。在这里,可以指定哪些URL需要身份验证、哪些 *可以匿名访问,还可以配置表单登录、HTTP基本认证、JWT认证等多种认证方式,以及自定义过滤器链、异常处理等 */ protected void configure(HttpSecurity http) throws Exception ; 
  2. 写自己的Security配置类
    ##记得加@EnableWebSecurity注解##

    @Configuration @EnableWebSecurity  public class SecurityConfig extends WebSecurityConfigurerAdapter {      /**      * 从 Spring5 开始,强制要求密码要加密      * BCryptPasswordEncoder是Spring Security提供的一个密码加密工具类,      * 它实现了PasswordEncoder接口,用于对用户的密码进行安全哈希加密存储,增强了系统的安全性。       */     @Bean     PasswordEncoder passwordEncoder(){         return new BCryptPasswordEncoder();     }     @Override     protected void configure(AuthenticationManagerBuilder auth) throws Exception {         auth.inMemoryAuthentication()                 //配置了两个用户,包括用户的用户名、角色、密码,用户密码已经加密(123)                 .withUser("zhangsan")                 //角色                 .roles("BOSS")                 //密码已经加密,可以通过 passwordEncoder.encode("123")获取                 .password("$2a$10$dQLFreAJHM0F.4XAWQMTA.kB5W3H2.hjA6xBUJFHTFT7iHRzO0flm")                 //连接方法                 .and()                 .withUser("lisi")                 .roles("EMPLOYEE")                 .password("$2a$10$RDdoj3sm/RD7HzqSnU864eEE5kEZZxbyQqnYQJGrO2pgkUGCDutTC");     } } 

    重启应用就可以通过zhangsan和lisi登陆了,虽然实现认证功能,但是受保护的资源都是一样的,只要登陆了,所有的接口都能访问。

    但实际企业开发过程中我们要根据进行角色管理,如果要实现这些功能 就需要重写 WebSecurityConfigurerAdapter 中的 HttpSecurity 方法:

    @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {     @Override     protected void configure(HttpSecurity http) throws Exception {         http.authorizeRequests() //开启登录配置                 // 使用antMatchers 匹配请求路径                 .antMatchers("/public/**").permitAll()  // 允许所有人访问以 "/public/" 开头的资源                 //访问以 "/boss/" 开头的资源需要 "boss" 角色                 .antMatchers("/boss/**").hasRole("ROLE_boss")                 //访问以 "/employee/" 开头的资源,必须具备 boos、manager 的角色                 .antMatchers("/employee/**").access("hasAnyRole('ROLE_boss','ROLE_manager')")                 // 访问以 "/api/" 开头的资源需要经过身份验证                 .antMatchers("/api/**").authenticated()                 // 任何其他请求也需要经过身份验证                 .anyRequest().authenticated()                  //开启表单登录                 .and().formLogin();     } } 
  3. 书写接口验证
    验证接口

  4. 使用注解简化
    上面介绍的认证与授权都是基于 URL 的,也可以通过注解来灵活地配置方法安全,要使用相关注解,首先要通过 @EnableGlobalMethodSecurity 注解开启基于注解的安全配置:

    @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true,securedEnabled=true) public class SecurityConfig extends WebSecurityConfigurerAdapter {  } 

    (1) prePostEnabled=true

    开启后会解锁 @PreAuthorize@PostAuthorize 两个注解

    • @PreAuthorize
      会在执行方法前验证, 推荐使用 @PreAuthorize 因为支持Spring EL表达式
      @PreAuthorize("hasRole('ROLE_ADMIN') and hasRole('ROLE_USER')")  //必须有全部的权限才可以访问 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')") //至少有一个即可访问 @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER')")  // 同上  
    • @PostAuthorize
      会在执行方法后验证

    (2) securedEnabled=true

    开启后会解锁 @Secured 注解,是基于角色的简单访问控制,Spring Security 3.x 之后不推荐。
    判断是否具有角色,注意这里匹配的字符串需要添加前缀 “ROLE_”

        @Secured({ "ROLE_admin", "ROLE_user" })     @Secured({"ROLE_admin"})     @GetMapping(value = "/test")     public String testSecured() {         return "testSecured";     } 
  5. 验证
    得要把之前的 配置移除掉

     // .antMatchers("/bossapi/**").hasRole("boss") 都移除掉 

    验证

5. Spring Security 结合 JWT使用

  1. 为什么要结合JWT使用?

    Spring Security与JWT的结合在分布式系统、移动端和单页应用等场景下具备灵活性、可扩展性和安全性,因此被广泛应用于现代Web应用程序的身份验证和授权中。

    JWT有不可替代的优势,但Security也有使用的便利性(已内置了很多权限验证功能),所以我们要结合一起使用,结合的时候,我们只使用Security鉴权功能,登陆功能不需要,因为我们已经自己实现了JWT相关的登陆接口,只是登陆成功后我们要把用户的角色Role写进token就可以了。

  2. 角色Role认证

    默认情况下,UsernamePasswordAuthenticationFilter 处理的是 POST 请求到 /login 路径, 我们可以模仿 UsernamePasswordAuthenticationFilter 书写自己的过滤器

    @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = false) // 开启注解 public class SecurityConfig extends WebSecurityConfigurerAdapter {     @Override     protected void configure(HttpSecurity http) throws Exception {         http.csrf().disable();         http.logout().disable();         http.formLogin().disable();         http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);// 禁用session          http.authorizeRequests().anyRequest().authenticated();// 所有请求都需要认证         // 添加自定义的过滤器         http.addFilterBefore(new JwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);          // 配置HTTP安全模块的异常处理,指定未授权访问时的处理逻辑         http.exceptionHandling().authenticationEntryPoint((HttpServletRequest request, HttpServletResponse response,                                                            AuthenticationException authException)->{             response.setContentType("application/json;charset=utf-8");             response.getWriter().write("{\"code\":403,\"message\":\"未授权\"}");         });     }      private class JwtTokenFilter extends BasicAuthenticationFilter {          public JwtTokenFilter() throws Exception {             super(authenticationManager());         }          @Override         protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {             // 从请求头中获取认证令牌             String token = request.getHeader("token");              // 初始化一个空的权限列表             List authorityList = new ArrayList<>();              // 如果令牌存在且不为空             if (ObjectUtil.isNotEmpty(token)) {                 // 解析令牌中的载荷信息                 JSONObject jsonObject = JSONUtil.parseObj(JWTUtil.parseToken(token).getPayload().toString());                 // 获取用户角色信息                 JSONArray roles = jsonObject.getJSONArray("roles");                  // 如果角色信息存在且不为空                 if (ObjectUtil.isNotEmpty(roles)) {                     // 遍历角色信息,构建权限列表                     for (Object role : roles) {                         SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.toString());                         authorityList.add(authority);                     }                 }             }              // 创建认证对象,用于存放权限列表             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("唯一", null, authorityList);             // 将认证对象设置到当前的安全上下文中             SecurityContextHolder.getContext().setAuthentication(authenticationToken);              // 继续执行过滤器链中的下一个过滤器             chain.doFilter(request, response);         }     } } 

相关内容

热门资讯

黑科技辅助!wpk辅助神器(透... 黑科技辅助!wpk辅助神器(透视)软件透明辅助挂(本来是真的有挂)-哔哩哔哩是一款可以让一直输的玩家...
5分钟了解“创思维正版辅助器下... 5分钟了解“创思维正版辅助器下载”详细透视开挂辅助安装-哔哩哔哩;一、创思维正版辅助器下载有挂的是的...
两分钟科普!wpk真吗,哈糖大... 两分钟科普!wpk真吗,哈糖大菠萝可以开挂吗,曝光教程(发现有挂)-哔哩哔哩哈糖大菠萝可以开挂吗辅助...
第一分钟了解(昆仑大厅)外挂辅... 第一分钟了解(昆仑大厅)外挂辅助插件(透视)详细教程(2022已更新)(哔哩哔哩);亲真的是有正版授...
黑科技辅助!wpk俱乐部长期盈... 黑科技辅助!wpk俱乐部长期盈利打法(透视)软件透明挂黑科技(切实存在有挂)-哔哩哔哩;1、让任何用...
第6分钟了解“功夫川嘛辅助器”... 第6分钟了解“功夫川嘛辅助器”详细透视开挂辅助器-哔哩哔哩;人气非常高,ai更新快且高清可以动的一个...
第五分钟辅助!xpoker辅助... 第五分钟辅助!xpoker辅助,德州透视插件,攻略教程(有挂方法)-哔哩哔哩德州透视插件辅助器中分为...
两分钟了解(皮皮跑胡子)外挂透... 两分钟了解(皮皮跑胡子)外挂透明挂辅助工具(辅助挂)透明挂教程(2020已更新)(哔哩哔哩);皮皮跑...
黑科技辅助!微扑克可以加入俱乐... 您好,微扑克可以加入俱乐部这款游戏可以开挂的,确实是有挂的,需要了解加微【136704302】很多玩...
8分钟了解“掌中乐游戏中心辅助... 8分钟了解“掌中乐游戏中心辅助器”详细透视开挂辅助脚本-哔哩哔哩;1、这是跨平台的掌中乐游戏中心辅助...