欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > java每日精进1.31(SpringSecurity)

java每日精进1.31(SpringSecurity)

2025/2/3 13:45:27 来源:https://blog.csdn.net/weixin_51721783/article/details/145408191  浏览:    关键词:java每日精进1.31(SpringSecurity)

在所有的开发的系统中,都必须做认证(authentication)和授权(authorization),以保证系统的安全性。

一、基础使用

1.依赖

<dependencies><!-- 实现对 Spring MVC 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 实现对 Spring Security 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency></dependencies>

2.application.yml

spring:# Spring Security 配置项,对应 SecurityProperties 配置类security:# 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。user:name: user # 账号password: user # 密码roles: ADMIN # 拥有角色

3.Controller

@RestController
@RequestMapping("/admin")
public class AdminController {@GetMapping("/demo")public String demo() {return "success!";}}

项目启动成功后,浏览器访问 http://127.0.0.1:8080/admin/demo 接口。因为未登录,所以被 Spring Security 拦截到登录界面。

因为我们没有自定义登录界面,所以默认会使用  DefaultLoginPageGeneratingFilter  类,生成上述界面。

登录完成后,因为 Spring Security 会记录被拦截的访问地址,所以浏览器自动动跳转返回界面;

二、自定义规则(自定义 Spring Security 的配置,实现权限控制。)

1.SecurityConfig

创建 SecurityConfig 配置类,继承 WebSecurityConfigurerAdapter 抽象类,实现 Spring Security 在 Web 场景下的自定义配置。代码如下:

// SecurityConfig.java@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {// ...}

重写 #configure(AuthenticationManagerBuilder auth) 方法,实现 AuthenticationManager 认证管理器。

// SecurityConfig.java@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.//使用内存中的 InMemoryUserDetailsManagerinMemoryAuthentication()//不使用 PasswordEncoder 密码编码器.passwordEncoder(NoOpPasswordEncoder.getInstance())//  配置 admin 用户.withUser("admin").password("admin").roles("ADMIN")// 配置 normal 用户.and().withUser("normal").password("normal").roles("NORMAL");
}

重写 #configure(HttpSecurity http) 方法,主要配置 URL 的权限控制。代码如下:

// SecurityConfig.java@Override
protected void configure(HttpSecurity http) throws Exception {http// <X> 配置请求地址的权限.authorizeRequests().antMatchers("/test/echo").permitAll() // 所有用户可访问.antMatchers("/test/admin").hasRole("ADMIN") // 需要 ADMIN 角色.antMatchers("/test/normal").access("hasRole('ROLE_NORMAL')") // 需要 NORMAL 角色。// 任何请求,访问的用户都需要经过认证.anyRequest().authenticated().and()// <Y> 设置 Form 表单登录.formLogin()
//                    .loginPage("/login") // 登录 URL 地址.permitAll() // 所有用户可访问.and()// 配置退出相关.logout()
//                    .logoutUrl("/logout") // 退出 URL 地址.permitAll(); // 所有用户可访问
}

调用 HttpSecurity#authorizeRequests() 方法,开始配置 URL 的权限控制。注意看艿艿配置的四个权限控制的配置。下面,是配置权限控制会使用到的方法:

  • #(String... antPatterns) 方法,配置匹配的 URL 地址,可传入多个。
  • 【常用】#permitAll() 方法,所有用户可访问。
  • 【常用】#denyAll() 方法,所有用户不可访问。
  • 【常用】#authenticated() 方法,登录用户可访问。
  • #anonymous() 方法,无需登录,即匿名用户可访问。
  • #rememberMe() 方法,通过remember me  登录的用户可访问。
  • #fullyAuthenticated() 方法,非remember me 登录的用户可访问。
  • #hasIpAddress(String ipaddressExpression) 方法,来自指定 IP 表达式的用户可访问。
  • 【常用】#hasRole(String role) 方法, 拥有指定角色的用户可访问。
  • 【常用】#hasAnyRole(String... roles) 方法,拥有指定任一角色的用户可访问。
  • 【常用】#hasAuthority(String authority) 方法,拥有指定权限(authority)的用户可访问。
  • 【常用】#hasAuthority(String... authorities) 方法,拥有指定任一权限(authority)的用户可访问。
  • 【最牛】#access(String attribute) 方法,执行结果为 true 时,可以访问。

2.Controller

@RestController
@RequestMapping("/test")
public class TestController {@GetMapping("/echo")public String demo() {return "示例返回";}@GetMapping("/home")public String home() {return "我是首页";}@GetMapping("/admin")public String admin() {return "我是管理员";}@GetMapping("/normal")public String normal() {return "我是普通用户";}}
  • 对于 /test/echo 接口,直接访问,无需登录。
  • 对于 /test/home 接口,无法直接访问,需要进行登录。
  • 对于 /test/admin 接口,需要登录「admin/admin」用户,因为需要 ADMIN 角色。
  • 对于 /test/normal 接口,需要登录「normal/normal」用户,因为需要 NORMAL 角色。

3.Spring Security 的注解,实现权限控制

1.修改权限配置类 

修该 security config 配置类,增加 @EnableGlobalMethodSecurity 注解,开启对 Spring Security 注解的方法,进行权限验证。代码如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter

然后即可添加方法级别的权限验证

@PreAuthorize("hasRole('ROLE_NORMAL')")@GetMapping("/normal")public String normal() {return "我是普通用户";}

三、Spring Session

Session 的一致性,简单来理解,就是相同 sessionid 在多个 Web 容器下,Session 的数据要一致。

我们先以用户使用浏览器,Web 服务器为单台 TomcatA 举例子。

  • 浏览器在第一次访问 Web 服务器 TomcatA 时,TomcatA 会发现请求的 Cookie 中存在 sessionid ,所以创建一个 sessionid 为 X 的 Session ,同时将该 sessionid 写回给浏览器的 Cookie 中。
  • 浏览器在下一次访问 Web 服务器 TomcatA 时,TomcatA 会发现请求的 Cookie 中存在 sessionid 为 X ,则直接获得 X 对应的 Session 。

但是如果情况如下:

  • 浏览器访问 TomcatA ,获得 sessionid 为 X 。同时,在多台 Tomcat 的情况下,我们需要采用 Nginx 做负载均衡。
  • 浏览器又发起一次请求访问 Web 服务器,Nginx 负载均衡转发请求到 TomcatB 上。TomcatB 会发现请求的 Cookie 中存在 sessionid 为 X ,则直接获得 X 对应的 Session 。结果呢,找不到 X 对应的 Session ,只好创建一个 sessionid 为 X 的 Session 。
  • 此时,虽然说浏览器的 sessionid 是 X ,但是对应到两个 Tomcat 中两个 Session 。那么,如果在 TomcatA 上做的 Session 修改,TomcatB 的 Session 还是原样,这样就会出现 Session 不一致的问题。

三种解决方案:

第一种,Session 黏连

使用 Nginx 实现会话黏连,将相同 sessionid 的浏览器所发起的请求,转发到同一台服务器。这样,就不会存在多个 Web 服务器创建多个 Session 的情况,也就不会发生 Session 不一致的问题。

不过,这种方式目前基本不被采用。因为,如果一台服务器重启,那么会导致转发到这个服务器上的 Session 全部丢失。

第二种,Session 复制

Web 服务器之间,进行 Session 复制同步。仅仅适用于实现 Session 复制的 Web 容器,例如说 Tomcat 、Weblogic 等等。

不过,这种方式目前基本也不被采用。试想一下,如果我们有 5 台 Web 服务器,所有的 Session 都要同步到每一个节点上,一个是效率低,一个是浪费内存。

第三种,Session 外部化存储

不同于上述的两种方案,Session 外部化存储,考虑不再采用 Web 容器的内存中存储 Session ,而是将 Session 存储外部化,持久化到 MySQL、Redis、MongoDB 等等数据库中。这样,Tomcat 就可以无状态化,专注提供 Web 服务或者 API 接口,未来拓展扩容也变得更加容易。

而实现 Session 外部化存储也有两种方式:

① 基于 Tomcat、Jetty 等 Web 容器自带的拓展,使用读取外部存储器的 Session 管理器。例如说:tomcat 使用 Redis 存储 Session 、实现 Jetty 使用 MySQL、MongoDB 存储 Session 。

② 基于应用层封装 HttpServletRequest 请求对象,包装成自己的 RequestWrapper 对象,从而让实现调用 HttpServletRequest#getSession() 方法时,获得读写外部存储器的 SessionWrapper 对象。

依赖:

<dependencies><!-- 实现对 Spring MVC 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 实现对 Spring Session 使用 Redis 作为数据源的自动化配置 --><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency><!-- 实现对 Spring Data Redis 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><!-- 去掉对 Lettuce 的依赖,因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 --><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><!-- 引入 Jedis 的依赖,这样 Spring Boot 实现对 Jedis 的自动化配置 --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><!-- 实现对 Spring Security 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency></dependencies>

配置文件:

spring:# 对应 RedisProperties 类redis:host: 127.0.0.1port: 6379password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码!database: 0 # Redis 数据库号,默认为 0 。timeout: 0 # Redis 连接超时时间,单位:毫秒。# 对应 RedisProperties.Jedis 内部类jedis:pool:max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。max-idle: 8 # 默认连接数最大空闲的连接数,默认为 8 。使用负数表示没有限制。min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。# 对应 SecurityProperties 类security:user: # 配置内存中,可登录的用户名和密码name: nihaopassword: nihao

SessionConfiguration:

@Configuration
@EnableRedisHttpSession // 自动化配置 Spring Session 使用 Redis 作为数据源
public class SessionConfiguration {}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com