一、创建项目

1、创建Spring Boot项目

配置pom文件

<!--Spring Boot WEB依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring Boot 测试依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!--Spring Boot MyBatis依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>
<!--数据库驱动依赖-->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

2、配置数据库和MyBatis

(1)初始化数据库并配置开发环境

数据库直接用图形化工具导入sql文件,设置当前的环境为开发环境

# 设置当前的环境为开发环境
spring:
  profiles:
    active: dev

application-dev.yml文件:

#设置项目端口号
server:
  port: 9081

#数据库环境配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/coolshark?useSSL=false&serverTimeZone=Asia/Shanghai
    username: root
    password: root

#日志级别
logging:
  level:
    cn.highedu.coolshark: debug

(2)配置MyBatis接口扫描

在application.yml中添加扫描

# 设置当前的环境为开发环境
spring:
  profiles:
    active: dev

#mybatis设置 驼峰表示
mybatis:
  mapper-locations: classpath:mappers/*.xml
  configuration: 
    map-underscore-to-camel-case: true 

3、使用Lombok

使用Lombok提供的注解,它不仅可以简化日志的创建,还可以提供许多其他简化编程的注解。

Lombok提供了一些常用的注解,如@Getter、@Setter、@ToString、@EqualsAndHashCode等。使用这些注解可以自动生成相应的代码。例如,使用@Getter注解可以自动生成属性的Getter方法,使用@Setter注解可以自动生成属性的Setter方法,使用@ToString注解可以自动生成toString方法等。

(1)使用步骤

  • 安装Lombok插件:在IntelliJ IDEA中安装Lombok插件。打开File->Settings->Plugins,在搜索栏中输入Lombok,然后点击Install安装Lombok插件。
  • 添加Lombok依赖:在Maven或Gradle的pom.xml文件中添加Lombok的依赖。
  • 在Java类中使用Lombok注解:在Java类中使用Lombok注解来简化代码,例如使用@Data注解自动生成Getter、Setter、toString、equals、hashCode等方法。
  • 在IDEA中开启Lombok支持:在IDEA中打开File->Settings->Build,Execution,Deployment->Compiler->Annotation Processors,勾选Enable annotation processing选项,然后在下方的Processor path中选择Lombok的jar包路径。
  • 清除缓存:在IDEA中清除缓存,打开File->Invalidate Caches / Restart。

添加依赖:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>

在测试类中使用注解:(以后不用再创建logger,直接添加注解即可)

@SpringBootTest
@Slf4j
public class DataSourceTest {
    //注入数据源 spring 帮我们去生成的
    @Autowired(required = false)
    private DataSource dateSource;

    @Test
    public void getConnect() throws SQLException {
        log.info("获取数据库连接测试");
        log.info("数据库连接:{}", dateSource.getConnection());
        log.info("获取数据库连接成功");
    }
}

创建User实体示例:

@Data //类中所有的属性自动生成getter、setter、toString等方法
@AllArgsConstructor //包含所有参数的构造方法
@NoArgsConstructor //生成无参构造
public class User {
    private Long id;
    private String name;
    private Integer age;
}

测试类:

@SpringBootTest
@Slf4j
public class LombokTest {

    @Test
    public void createUser() {
        User user1 = new User();
        user1.setId(1L);
        user1.setName("Tom");
        user1.setAge(20);
        log.info("user1:{}", user1);
        User user2 = new User(2L, "Jerry", 20);
        log.info("user2:{}", user2);

    }
}

(2)Lombok的缺点

  • 工具支持:Lombok依赖于编译时代码生成技术,因此在一些IDE或工具链不支持的情况下,可能会存在一些问题。
  • 潜在风险:Lombok生成的代码可能会影响IDE的代码分析能力,可能会隐藏代码缺陷和潜在的问题,需要开发者仔细检查和测试。
  • 不利于团队协作:Lombok会生成大量的代码,可能会导致代码库的混乱和难以阅读,不利于团队协作和代码维护。

(3)Lombok的全部注解

  • @Data:注解在类上,自动为类生成getter、setter、toString、equals、hashCode等方法。
  • @Getter:注解在属性上,自动生成getter方法。
  • @Setter:注解在属性上,自动生成setter方法。
  • @ToString:注解在类上,自动生成toString方法。
  • @EqualsAndHashCode:注解在类上,自动生成equals和hashCode方法。
  • @NoArgsConstructor:注解在类上,自动生成无参构造方法。
  • @AllArgsConstructor:注解在类上,自动生成全参构造方法。
  • @RequiredArgsConstructor:注解在类上,自动生成构造方法,只对final或@NonNull属性生效。
  • @Builder:注解在类上,自动生成builder方法。
  • @Log:注解在类上,自动生成Logger对象。
  • @Log4j2:注解在类上,自动生成Log4j2 Logger对象。
  • @Slf4j:注解在类上,自动生成Slf4j Logger对象。
  • @Cleanup:注解在局部变量上,自动为局部变量调用close或者cleanup方法。
  • @SneakyThrows:注解在方法上,将方法中的异常转化为unchecked异常。
  • @Synchronized:注解在方法上,将方法转化为同步方法。
  • @NonNull:注解在参数和属性上,表示参数和属性不能为空。
  • @Nullable:注解在参数和属性上,表示参数和属性可以为空。
  • @Value:注解在类上,与@Data类似,不过它会生成一个不可变的类。
  • @Delegate:注解在属性上,将该属性的调用转发到指定对象上。
  • @UtilityClass:注解在类上,表示该类是一个工具类,自动为类添加私有构造方法和静态方法。

二、Spring Security框架

1、使用SpringSecurity进行权限控制

其提供了一系列的功能用于保护Java Web应用程序,防止未经授权的访问

Spring Security 还提供了其他一些重要的功能和扩展,如:

  • 基于注解的安全性:通过 Spring Security 的注解,可以为应用程序中的方法、类、参数等设置安全性规则,以便在运行时实施安全策略。
  • CSRF(跨站请求伪造)保护:Spring Security 提供了一些内置的安全措施,以防止跨站点攻击,如 CSRF 保护、Clickjacking 防御、安全 HTTP 标头等。
  • 单点登录(SSO):Spring Security 提供了与许多 SSO 解决方案集成的能力,如 CAS、OAuth2、OpenID Connect 等,以便为多个应用程序提供一致的身份验证和授权机制。
  • 集成Spring框架:Spring Security 可以与 Spring 框架的其他模块和组件无缝集成,如 Spring MVC、Spring Boot、Spring Data 等,以便更方便地构建和维护安全的 Web 应用程序。

(1)认证和授权

认证是确定用户、设备或系统的身份是否真实

授权是决定哪些身份可以访问那些资源的过程

认证和授权

(2)Spring Security 使用步骤

1、 添加 Spring Security 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、配置安全规则

Spring Security配置可以分为两个主要方面:认证规则和授权规则。

认证规则确定了用户如何登录到应用程序。你可以配置基本的用户名和密码认证规则,也可以定义自定义认证规则。

授权规则决定了哪些用户可以访问应用程序的哪些部分。这包括Web请求的授权和方法级别的授权。

2、Spring Security认证

(1)配置临时认证规则

在dev.yml中添加配置

#设置项目端口号
server:
  port: 9081

#数据库环境配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/coolshark?useSSL=false&serverTimeZone=Asia/Shanghai
    username: root
    password: root
  #配置认证的用户名和密码
  security:
    user:
      name: tom
      password: 123456

#日志级别
logging:
  level:
    cn.highedu.csmall: debug

(2)配置密码加密

密码加密技术是一种将密码转换成不可逆的、难以破解的密文的方法,以保证用户密码的安全性。以下是一些主要的密码加密技术:

  • MD5(Message Digest Algorithm 5):一种广泛使用的密码哈希函数,将任意长度的输入(明文)通过哈希算法,输出固定长度的哈希值(密文),不可逆。
  • SHA-2(Secure Hash Algorithm 2):SHA-2 包含多个算法,比如 SHA-256,SHA-384 和 SHA-512,用于计算信息的哈希值,是一种更安全的哈希算法,比 MD5 更难以破解。
  • BCrypt:一种基于 Blowfish 加密算法的密码哈希函数,加密后的密文长度固定为 60 个字符,可以设置迭代次数,防止暴力破解。
  • PBKDF2(Password-Based Key Derivation Function 2):一种基于密码的密钥派生函数,可以根据一个密码和一个盐生成密钥,提高密码的安全性。
/**
 * 设置密码加密规则
 */
@Configuration //声明spring管理类
@Slf4j
public class PasswordEncoderConfig {
    @Bean
    public PasswordEncoder passwordEncoder() {
        //密码生成器
        PasswordEncoder passwordEncoder =
                PasswordEncoderFactories.createDelegatingPasswordEncoder();
        log.info("创建密码生成器:{}", passwordEncoder.getClass().getName());
        return passwordEncoder;
    }
}

测试类:

@SpringBootTest
@Slf4j
public class PasswordEncoderTest {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Test
    public void encoder(){
        String password = "123456";
        String encodePassword = passwordEncoder.encode(password);
        log.info("密码明文:{}",password);
        log.info("密码密文:{}",encodePassword);
    }
}

测试结果:

: 创建密码生成器:org.springframework.security.crypto.password.DelegatingPasswordEncoder
: Adding welcome page: class path resource [static/index.html]
: Started PasswordEncoderTest in 1.732 seconds (process running for 2.511)
: 密码明文:123456
: 密码密文:{bcrypt}$2a$10$FMA8Sa3E/VZKRUhxnGoDpu7j5sEQRg8WqymLtPpf5r2/vM1ApdHGm

(3)提供数据进行认证

Spring Security 提供了基于程序提供的认证数据存储方式,需要用户实现 UserDetailsService 接口和 UserDetails 接口。

UserDetailsService 接口用于获取用户的认证信息

UserDetails 接口则用于描述用户的具体信息,如用户名、密码、用户角色等等。

(4)实现用户信息接口

实现UserDetails接口

/**
 * 存储用户信息
 * 返回用户信息详情(用户名、密码、权限)
 */

public class TestUserDetail implements UserDetails {
    //定义用户名和密码
    private String username = "tom";
    private String password = "{bcrypt}$2a$10$FMA8Sa3E/VZKRUhxnGoDpu7j5sEQRg8WqymLtPpf5r2/vM1ApdHGm";

    /**
     * 返回用户的权限 如果返回null 表明用户没有任何权限
     * 用户tom具有admin和user两个角色,同时具有查询和添加数据的权限
     *
     * @return 权限的集合
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        //添加角色
        authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        //添加权限
        authorities.add(new SimpleGrantedAuthority("sys:log:query")); //查询日志的权限
        authorities.add(new SimpleGrantedAuthority("sys:log:add"));//添加日志的权限
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    /**
     * 返回false表示用户过期
     *
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 用户是否被锁定 返回false表示用户不被锁定
     *
     * @return
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 以下方法返回false,表示用户凭证已过期
     *
     * @return true
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

实现 UserDetailsService接口:

/**
 * 获取用户信息
 */
@Component
@Slf4j
public class TestUserDetailService implements UserDetailsService {
    /**
     * 根据用户名查询用户详情信息
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (username.equals("tom")) {
            log.info("用户名tom存在,准备返回信息");
            return new TestUserDetail();
        }
        throw new UsernameNotFoundException("用户名:" + username + "不存在");
    }
}

3、 Spring Security方法授权

(1)授权信息

Spring Security 的授权功能用于管理用户对资源的权限,它基于角色和权限来控制对方法Web资源的访问。

Spring Security 授权功能允许你控制哪些用户可以访问哪些资源,以及哪些方法可以被哪些用户访问。

(2)【案例】添加角色和权限

/**
 * 返回用户的权限 如果返回null 表明用户没有任何权限
 * 用户tom具有admin和user两个角色,同时具有查询和添加数据的权限
 *
 * @return 权限的集合
 */
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    List<GrantedAuthority> authorities = new ArrayList<>();
    //添加角色
    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
    authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
    //添加权限
    authorities.add(new SimpleGrantedAuthority("sys:log:query")); //查询日志的权限
    authorities.add(new SimpleGrantedAuthority("sys:log:add"));//添加日志的权限
    return authorities;
}

(3)方法角色授权

Spring Security 提供方法级别的角色授权,用于保护应用程序中的方法,以防止未经授权的访问。通过在方法级别配置角色授权,你可以确保只有拥有特定角色的用户才能访问受保护的方法。这有助于保护应用程序中的关键功能和数据,确保只有授权用户可以访问和修改。

创建管理类,开启方法授权:

@Configuration
@EnableMethodSecurity //开启方法授权(5.7以及以后的版本)
//@EnableGlobalMethodSecurity(prePostEnabled = true) //spring 5.7之前的版本开启授权
public class MethodSecurityConfig {
    
}

做service:

@Service
@Slf4j
public class LogService {
    /**
     * 管理员日志方法 必须具有管理员角色才能访问
     *
     * @PreAuthorize 表示方法授权
     * hasRole检查用户是否具有角色
     */
    @PreAuthorize("hasRole('ADMIN')") //有管理员角色才能访问
    public void adminLog() {
        log.info("管理员日志方法");
    }

    /**
     * 经理日志方法 必须具有经理角色才能访问
     */
    @PreAuthorize("hasRole('MANAGER')")
    public void managerLog() {
        log.info("经理日志方法");
    }
}

controller层:

@RestController
@Slf4j
public class LoggerController {
    @Autowired
    private LogService logService;

    /**
     * 访问请求admin/log 必须具有ADMIN角色
     *
     * @return
     */
    @GetMapping("/admin/log")
    public String adminLog() {
        log.info("请求到达/admin/log");
        logService.adminLog();
        return "管理员查询了日志";
    }

    /**
     * 访问请求/manager/log 必须具有manager角色才能访问
     * @return
     */
    @GetMapping("/manager/log")
    public String managerLog() {
        log.info("请求到达/manager/log");
        logService.managerLog();
        return "经理查询了日志";
    }
}

测试效果:

测试效果1

(4)方法权限授权

一个角色的范围往往是很大的,一个用户可以分配多个角色。

权限控制往往不变

在LogService中添加方法(对权限进行操作)

/**
 * 具有日志查询权限的用户才能访问
 */
@PreAuthorize("hasAnyAuthority('sys:log:query')")
public void queryList() {
    log.info("日志查询方法");
}

@PreAuthorize("hasAuthority('sys:log:delete')")
public void delete() {
    log.debug("日志删除方法");
}

控制器中定义访问这两个方法的入口:

@GetMapping("/log/query")
public String queryList(){
    log.info("请求到达:/log/query");
    logService.queryList();
    return "查询了日志";
}

@GetMapping("/log/delete")
public String delete(){
    log.info("请求到达:/log/delete");
    logService.delete();
    return "删除了日志";
}

4、Web资源授权

(1)Web授权原理

Spring Security 提供了 Web 资源保护功能,它通过一系列的 Web 过滤器来实现 。这些过滤器可以拦截所有的 Web 请求,并根据配置来进行安全处理。

web授权原理

(2)HTTP和Web授权配置

configure(HttpSecurity http) 方法用于配置过滤器链的规则,你可以在这里设置哪些 URL 需要授权以及授权的方式等。

configure(WebSecurity web) 方法可以配置绕过过滤器链的规则,比如设置哪些 URL 不需要被 Spring Security 保护。

从 Spring Security 5.7 开始,已经弃用了继承 WebSecurityConfigurerAdapter 的方式,而是采用了创建 SecurityFilterChain 类型的 Bean 来配置 Web 安全

配置管理类;

@Configuration
@EnableWebSecurity //开启web资源授权
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // /admin/index.html资源具有admin角色才能访问
        http.authorizeHttpRequests()
                .requestMatchers("/admin/index/html").hasRole("ADMIN");
        http.authorizeHttpRequests()
                .requestMatchers("/manager/index.html").hasRole("MANAGER");
        return http.build();
    }
}

测试效果展示:

web拦截

(3)Web权限匹配模式

在一个网站项目中,可能会有数以千计的不同的URL请求,使用URL匹配模式可以大大简化授权配置的工作。

Spring Security 引入了 Ant 路径匹配,以便用户可以使用通配符(*)来匹配请求 URL 的路径:

* 用于匹配路径中的单个字符或单个路径,例如 /user/* 可以匹配 /user/list 或 /user/edit,但不能匹配 /user/list/info。

用于匹配路径中的多个字符或多个路径,例如 /user/ 可以匹配 /user/list、/user/edit 和 /user/list/info。:

一般情况下都是两个*

@Configuration
@EnableWebSecurity //开启web资源授权
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //所有资源不需要登录都可以访问
        http.authorizeHttpRequests()
                .requestMatchers("/static/**", "/css/**", "/js/**").permitAll();
        // /admin/index.html资源具有admin角色才能访问
        http.authorizeHttpRequests()
                .requestMatchers("/admin/index.html").hasRole("ADMIN");
        http.authorizeHttpRequests()
                .requestMatchers("/manager/index.html").hasRole("MANAGER");


        http.authorizeHttpRequests()
                .requestMatchers("/user/index.html").hasRole("USER");
        //其他资源需要登录就可以访问
        http.authorizeHttpRequests().anyRequest().authenticated();
        http.formLogin().permitAll();//放开其他资源
        return http.build();
    }
}

除了 hasRole,Spring Security 还提供了以下一些方法用于授权:

  • hasAnyRole:判断当前用户是否拥有其中一个角色
  • hasAuthority:判断当前用户是否拥有某个权限
  • hasAnyAuthority:判断当前用户是否拥有其中一个权限
  • authenticated:判断当前用户是否已经认证,即是否已经登录
  • permitAll:允许所有用户访问
  • denyAll:拒绝所有用户访问
  • anonymous:允许匿名用户访问
  • rememberMe:允许通过 remember-me 自动登录的用户访问

(4)绕过安全过滤器

Spring Security 的 Web 授权功能是由一系列过滤器共同完成的。在上述配置中,虽然对路径 "/static/**" 使用了 permitAll() 来放行请求,但这些请求仍然会经过一系列安全过滤器的处理,这可能会对性能产生一定的影响。

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
   // /static/**、/css/**、/js/** 路径不需要认证, 即绕过认证过滤器
   return (web) -> web.debug(true).ignoring().requestMatchers("/static/**", "/css/**", "/js/**");
}
最后修改:2023 年 09 月 28 日
如果觉得我的文章对你有用,请随意赞赏