Spring Boot与Spring Security结合MyBatis构建安全的RESTful Web服务

为什么选择Spring Boot、Spring Security和MyBatis?

1. 简化配置和开发: Spring Boot通过自动配置大大减少了开发中的配置工作,而Spring Security提供了强大的安全特性。MyBatis是一种灵活的ORM框架,使用SQL语句进行数据操作,非常适合有复杂查询需求的项目。

2. 快速构建生产级应用: Spring Boot和Spring Security集成了很多企业级的特性,如身份认证、授权、加密等,使得应用更容易部署到生产环境。MyBatis简化了复杂SQL的管理和执行,使得数据操作更加高效。

3. 丰富的生态系统: Spring Boot与Spring Security无缝集成,提供了大量的扩展组件,方便实现各种复杂功能。MyBatis与Spring的结合也非常紧密,能够提供强大的数据持久化支持。

废话不多说,上代码!

1. 那么好,第一步:我们来创建一个名为 HelloController 的类来处理HTTP请求。
package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello, Spring Boot!";
    }
}
2. 第二步:我们来配置Spring Security

创建一个 SecurityConfig 类来配置Spring Security。

package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration // 标记该类是一个配置类
@EnableWebSecurity // 启用Spring Security的Web安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    /**
     * 配置HTTP请求的安全性
     * @param http HttpSecurity对象,用于配置Web安全
     * @throws Exception 抛出配置错误异常
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable() // 禁用CSRF保护
            .authorizeRequests()
            .antMatchers("/hello").permitAll() // 对于/hello请求,不需要认证
            .anyRequest().authenticated() // 其他任何请求都需要认证
            .and()
            .formLogin() // 启用表单登录
            .and()
            .httpBasic(); // 启用HTTP Basic认证
    }

    /**
     * 配置身份验证管理器
     * @param auth AuthenticationManagerBuilder对象,用于构建身份验证管理器
     * @throws Exception 抛出配置错误异常
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); // 使用自定义的用户身份验证服务和密码编码器
    }

    /**
     * 定义密码编码器
     * @return PasswordEncoder对象,用于加密用户密码
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 使用BCrypt对密码进行加密
    }
}

注解解释:

  • @Configuration: 标记该类为一个配置类。
  • @EnableWebSecurity: 启用Spring Security的Web安全支持。

配置解释:

  • csrf().disable(): 禁用CSRF保护,防止跨站请求伪造攻击。
  • authorizeRequests(): 配置基于HttpServletRequest的访问限制。
  • antMatchers("/hello").permitAll(): 对/hello请求允许所有访问,不需要认证。
  • anyRequest().authenticated(): 其他任何请求都需要认证。
  • formLogin(): 启用表单登录。
  • httpBasic(): 启用HTTP Basic认证。
  • AuthenticationManagerBuilder: 配置身份验证管理器,使用自定义的用户身份验证服务和密码编码器。
  • BCryptPasswordEncoder: 使用BCrypt对密码进行加密,增加密码安全性。
3. 第三步当然少不了数据库配置。配置MyBatis 

application.properties 文件中添加数据库的配置:

spring.datasource.url=xxxx // 配置成你自己数据库的URL
spring.datasource.driverClassName=xxx //配置成你自己的数据库驱动类
spring.datasource.username=xxx // 数据库用户名
spring.datasource.password=xxx // 数据库密码
4. 实体肯定也不能丢啦~ 创建一个 User 实体类:
package com.example.demo;

public class User {
    private Long id;
    private String username;
    private String password;

    // Getters and setters
}
5. 数据库查询肯定也要撒。创建一个MyBatis的Mapper接口 UserMapper
package com.example.demo;

import org.apache.ibatis.annotations.*;

@Mapper // 标记这是一个MyBatis的Mapper接口
public interface UserMapper {

    @Select("SELECT * FROM users WHERE username = #{username}")
    User findByUsername(String username); // 通过用户名查找用户

    @Insert("INSERT INTO users(username, password) VALUES(#{username}, #{password})")
    @Options(useGeneratedKeys = true, keyProperty = "id") // 自动生成主键
    void insert(User user); // 插入新用户
}
6. 业务逻辑层肯定需要的不。创建一个 UserService 类:
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service // 标记这是一个服务类
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 通过用户名查找用户
     * @param username 用户名
     * @return User对象
     */
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }

    /**
     * 保存用户
     * @param user User对象
     */
    public void save(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword())); // 加密用户密码
        userMapper.insert(user); // 插入用户到数据库
    }
}
7. 验证你在系统的身份信息肯定也少不了啦,用户身份验证服务

创建一个 CustomUserDetailsService 类来实现用户身份验证。

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service // 标记这是一个服务类
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserService userService;

    /**
     * 根据用户名加载用户
     * @param username 用户名
     * @return UserDetails对象
     * @throws UsernameNotFoundException 当用户未找到时抛出异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
    }
}

方法解释:

  • loadUserByUsername: 这个方法是根据用户名加载用户信息,如果用户不存在则抛出异常,返回一个UserDetails对象,包含用户名、密码和权限信息。
8. 追求优雅,错误处理肯定要啦!

当然了,为了完善我们的应用,我们需要处理可能发生的错误。毕竟我们程序员追求的就是优雅!

例如,当用户未找到时返回适当的错误信息。

创建一个 ResourceNotFoundException 类:

package com.example.demo;

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

创建一个 GlobalExceptionHandler 类来处理全局异常:

package com.example.demo;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice // 标记这是一个全局异常处理类
public class GlobalExceptionHandler {

    /**
     * 处理ResourceNotFoundException异常
     * @param ex ResourceNotFoundException对象
     * @return ResponseEntity对象,包含错误信息和HTTP状态码
     */
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

注解解释:

  • @RestControllerAdvice: 标记这是一个全局异常处理类,处理控制器中的异常。
  • @ExceptionHandler: 定义异常处理方法,当抛出 ResourceNotFoundException 时返回404状态码和错误信息。

方法解释:

  • handleResourceNotFoundException: 处理 ResourceNotFoundException 异常,返回包含错误信息和404状态码的响应。

带来的好处:

  • 一致性:所有异常都由统一的处理器处理,返回一致的错误信息。
  • 易于调试:更清晰的错误信息有助于快速定位问题。
好了好了,通过以上示例,我们展示了如何结合Spring Boot、Spring Security和MyBatis构建一个安全的RESTful Web服务,并介绍了自动配置、数据访问、和异常处理等关键概念。
创作不易,希望以上代码能给你带来帮助!

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值