基于SpringSecurity+JWT+Redis进行用户的认证和权限管理(一)

本文介绍了如何在SpringBoot项目中添加依赖,包括SpringBoot起步器、测试、Web、Security、MySQL、MyBatis、Lombok、JWT和Redis,同时配置了application.yml和实现UserDetailsService接口以支持SpringSecurity的用户认证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一步

我们需要建立一个springboot工程,然后进行依赖的添加 我这里的springboot工程的版本是2.7.14 引入下方的依赖时,没有引发依赖冲突

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

        <!--引入web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <!--引入springsecurity依赖 作用: 安全框架,进行用户的认证和权限的管理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!-- 引入Mysql依赖 我这里使用的是Mysql8.0的依赖 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!-- 引入Mybatis 作用: 和数据库进行交互 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!-- 引入lombok 作用: 减少代码量通过注解生成类的有参无参以及日志-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- 引入JWT 作用: 进行用户信息的加密-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>4.2.1</version>
        </dependency>

        <!-- 引入redis依赖 作用: 为了后面进行token验证 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.7.14</version>
        </dependency>

第二步

进行application.yml 配置文件的编写

# mysql注册  
# com.mysql.cj.jdbc.Driver       表示数据库的驱动 mysql8.0 驱动
# com.mysql.jdbc.Driver          这个是mysql5.xx 本版的驱动
# jdbc:mysql://127.0.0.1:3306    表示数据库的地址和端口号 我这里是本地连接。
# security_study                 表示的是数据库的名称
# useUnicode=true&characterEncoding=UTF-8&  设置字符编码集使用的是Unicode编码,字符集是UTF-8
#serverTimezone=Asia/Shanghai     设置时区   数据库8.0以上的版本需要设置时区,不设置时区会报错

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/security_study?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

# 配置Mybatis
mybatis:
  # 配置映射文件
  mapper-locations: classpath:mapper/*.xml
  # 配置实体映射包 作用: 在配置xml文件映射时我们可以不用写包名,直接写表名即可 下方的包表示的是项目    
  #中的实体类的包
  type-aliases-package: com.hxtz.springsecurity13demo.entity
  configuration:
    # 开启将下划线转成驼峰命名。 数据库中的表的字段一般以下划线命名,而java中的属性一般是以驼峰命名。这里将其进行了转换。
    map-underscore-to-camel-case: true
    # 开启控制台打印日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 配置签名密钥 这个密钥用于的是jwt签名密钥 内容是字符串可以自己随便写 我这里是MrCui 是我的署名
my:
  secretKey: "MrCui"

第三步 进行核心代码的编写。

  (1) 编写实体类要和数据库中的字段对应起来

这里用到了4个注解

@Data 帮我们生成了这个类的set() get() ,toString() 等方法

@NoArgsConstructor   生成类的无参构造
@AllArgsConstructor   生成类的有参构造
@Builder   将类改为建造者模式。

package com.hxtz.springsecurity13demo.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Author: cyb
 * @Date: 2023/8/3 10:35
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SysUser implements Serializable {
    private Integer userId;
    private String username;
    private String password;
    private String sex;
    private String address;
    private Integer enabled;
    private Integer accountNoExpired;
    private Integer credentialsNoExpired;
    private Integer accountNoLocked;
}
# 用户表
CREATE TABLE `sys_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `username` varchar(255) DEFAULT NULL COMMENT '登陆名',
  `password` varchar(255) DEFAULT NULL COMMENT '密码',
  `sex` varchar(255) DEFAULT NULL COMMENT '性别',
  `address` varchar(255) DEFAULT NULL COMMENT '地址',
  `enabled` int(11) DEFAULT '1' COMMENT '是否启动账户0禁用 1启用',
  `account_no_expired` int(11) DEFAULT '1' COMMENT '账户是否没有过期0已过期 1 正常',
  `credentials_no_expired` int(11) DEFAULT '1' COMMENT '密码是否没有过期0已过期 1 正常',
  `account_no_locked` int(11) DEFAULT '1' COMMENT '账户是否没有锁定0已锁定 1 正常',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
# 用户角色表
CREATE TABLE `sys_role_user` (
  `uid` int(11) NOT NULL COMMENT '用户编号',
  `rid` int(11) NOT NULL COMMENT '角色编号',
  PRIMARY KEY (`uid`,`rid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
# 角色菜单表
CREATE TABLE `sys_role_menu` (
  `rid` int(11) NOT NULL COMMENT '角色表的编号',
  `mid` int(11) NOT NULL COMMENT '菜单表的编号',
  PRIMARY KEY (`mid`,`rid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
# 角色表
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `rolename` varchar(255) DEFAULT NULL COMMENT '角色名称,英文名称',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
# 菜单表
CREATE TABLE `sys_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `pid` int(11) DEFAULT NULL COMMENT '父级编号',
  `name` varchar(255) DEFAULT NULL COMMENT '名称',
  `code` varchar(255) DEFAULT NULL COMMENT '权限编码',
  `type` int(11) DEFAULT NULL COMMENT '0代表菜单1权限2 url',
  `delete_flag` tinyint(4) DEFAULT '0' COMMENT '0代表未删除,1 代表已删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

(2)编写mapper层 查询用户信息用来进行用户的认证。

java文件中的 @Param("userName")  要和 #{userName} 对应

/**
 * @Author: cyb
 * @Date: 2023/8/4 16:29
 * 获取用户信息
 */
@Mapper
public interface SysUserMapper {
    /**
     *  查询用户信息
     * @param userName  这里的userName要和mapper文件对应起来。
     * @return
     */
    SysUser queryUserByUserName(@Param("userName") String userName);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hxtz.springsecurity13demo.mapper.SysUserMapper">
    <select id="queryUserByUserName" resultType="sysUser">
        select user_id,username,`password`,sex,address,enabled,account_no_expired,credentials_no_expired,account_no_locked FROM
            sys_user
        WHERE username = #{userName};
    </select>
</mapper>

(3)编写Service层代码

/**
 * @Author: cyb
 * @Date: 2023/8/4 16:42
 */
public interface SysUserService {
    SysUser queryUserByUserName(String userName);
}
/**
 * @Author: cyb
 * @Date: 2023/8/4 16:43
 */
@Service
public class SysUserServiceImpl implements SysUserService {
    @Resource
    private SysUserMapper sysUserMapper;
    @Override
    public SysUser queryUserByUserName(String userName) {
        SysUser sysUser = sysUserMapper.queryUserByUserName(userName);
        return sysUser;
    }
}

(4)  实现 UserDetails接口   该接口是提供用户信息的核心接口。该接口实现仅仅存储用户的信息。

为什么要实现这个接口?参考这篇文章  一起搞清楚 Spring Security 中的 UserDetails

package com.hxtz.springsecurity13demo.component;

import com.hxtz.springsecurity13demo.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;

/**
 * @Author: cyb
 * @Date: 2023/8/4 16:52
 */
@Service
public class MyUserDetails implements UserDetails {
    private SysUser sysUser;
    private List<SimpleGrantedAuthority> authorityList;
    public MyUserDetails() {
    }
    public MyUserDetails(SysUser sysUser) {
        this.sysUser = sysUser;
    }

    /**
     *  获取权限信息
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        return authorityList;
    }

    /**
     *  获取密码
     * @return
     */

    @Override
    public String getPassword() {
        String password = this.sysUser.getPassword();
        this.sysUser.setPassword(null);
        return password;
    }

    /**
     *  获取用户名
     * @return
     */

    @Override
    public String getUsername() {

        return this.sysUser.getUsername();
    }

    /**
     *  判断账户是否过期
     * @return
     */

    @Override
    public boolean isAccountNonExpired() {
        return this.sysUser.getAccountNoExpired().equals(1);
    }

    /**
     *  判单账户是否加锁
     * @return
     */

    @Override
    public boolean isAccountNonLocked() {
        return this.sysUser.getAccountNoLocked().equals(1);
    }

    /**
     *  判断账户凭证是否过期
     * @return
     */

    @Override
    public boolean isCredentialsNonExpired() {
        return this.sysUser.getCredentialsNoExpired().equals(1);
    }

    /**
     * 判断账户是否启用
     * @return
     */

    @Override
    public boolean isEnabled() {
        return this.sysUser.getEnabled().equals(1);
    }

    public void setAuthorityList(List<SimpleGrantedAuthority> authorityList) {
        this.authorityList = authorityList;
    }
    public SysUser getSysUser(){
        return sysUser;
    }
}

(5) 注册Bean到Spring容器中 主要做用是为了配置springSecurity

这里面注册了3个Bean

首先注册  UserDeatisService 这个类主要是用来返回用户的信息

其次注册了 PasswordEncoder 这个类主要是用来进行密码的加密解密的在新版本的springsecurity框架中规定必须要注册密码编译器

最后注册了 SecurityFilterChain 安全适配器 这个类主要是用来拦截请求的。 查看用户是否有访问这个的权限

SecurityFilterChain是security新版的做法,以前是我们通过匿名内部类来进行 WebSecurityConfigurerAdapter 这个类的编写。

package com.hxtz.springsecurity13demo.config;

import com.hxtz.springsecurity13demo.JwtCheckFilter;
import com.hxtz.springsecurity13demo.component.MyAuthenticationSuccessHandler;
import com.hxtz.springsecurity13demo.component.MyLogoutSuccessHandler;
import com.hxtz.springsecurity13demo.component.MyUserDetails;
import com.hxtz.springsecurity13demo.entity.SysUser;
import com.hxtz.springsecurity13demo.service.impl.SysMenuServiceImpl;
import com.hxtz.springsecurity13demo.service.impl.SysUserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: cyb
 * @Date: 2023/8/4 16:22
 * security配置类
 */
@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用全局方法权限处理
@Slf4j
public class MySecurityConfig {
    @Resource
    private SysUserServiceImpl userService;

    // @Resource 
    // private SysMenuServiceImpl sysMenuService;  // 目前还没有用到后续会用到 这个service主要用来查看用户的权限信息的。
    @Resource
    private MyAuthenticationSuccessHandler successHandler;
    @Resource
    private MyLogoutSuccessHandler logoutHandler;
    @Resource
    private JwtCheckFilter jwtCheckFilter;

    /**
     *  注册UserDetailsService
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        return username -> {
            // 在这里怎么处理username?
            SysUser sysUser = userService.queryUserByUserName(username);
            if (null==sysUser){
                throw new UsernameNotFoundException("用户不存在");
            }
/*  权限信息这里先不用管。目前我们只关注认证就可以了 后续会将这里进行修改。
            // 获取用户权限信息
            List<String> authList = sysMenuService.getUserMenuByUserId(sysUser.getUserId());
            List<SimpleGrantedAuthority> collect = authList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
           
            myUserDetails.setAuthorityList(collect); // 将用户的权限信息放入到用户详细信息中
*/
            //  这里是用户的信息
            MyUserDetails myUserDetails = new MyUserDetails(sysUser);
            
            return myUserDetails;
        };

    }

    /**
     *  注册密码编译器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     *  安全适配器
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable(); //  先关闭跨域请求。
        http.formLogin().successHandler(successHandler).permitAll(); // 请求登录接口放行。
        http.logout().logoutSuccessHandler(logoutHandler);
        http.authorizeRequests().anyRequest().authenticated(); // 任何的请求都需要认证。
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用session域
        http.addFilterBefore(jwtCheckFilter, UsernamePasswordAuthenticationFilter.class); // 将自定义过滤器放在 过滤器之前
        return http.build();
    }
}

<think>我们正在处理个关于SpringBoot整合SpringSecurityJWTRedis的实现示例的查询。根据用户的问题,我们需要提供个清晰的技术实现方案,包括必要的代码示例步骤说明。同时,我们还需要生成相关问题。我们将按照以下步骤进行:1.引入必要的依赖(SpringSecurity,JWT,Redis)2.配置SpringSecurityJWT3.实现用户认证授权4.整合Redis用于令牌管理(如令牌黑名单或缓存用户信息)注意:由于回答中需要包含代码,我们将使用Markdown代码块,并确保行内公式使用$...$,独立公式使用$$...$$(尽管在这个技术问题中可能用不到数学公式,但如果有数学表达式,我们会遵守规则)。另外,我们将使用中文回答,并在末尾生成相关问题。参考站内引用[^1]是关于SpringBootActuatorJolokia的,与当前问题关系不大;引用[^2]是关于前端技术的,也不直接相关。因此,我们将主要根据SpringBootSpringSecurityJWTRedis的标准实践来回答。开始构建回答:</think>### Spring Boot 整合 Spring Security + JWT + Redis 实现方案 以下是整合 Spring Security、JWT Redis 的技术实现步骤: #### 1. 添加依赖 (`pom.xml`) ```xml <dependencies> <!-- Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JWT 支持 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> ``` #### 2. 配置安全设置 (`SecurityConfig.java`) ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationFilter jwtFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` #### 3. JWT 工具类 (`JwtUtils.java`) ```java @Component public class JwtUtils { @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private int expiration; public String generateToken(UserDetails userDetails) { return Jwts.builder() .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(secret).parseClaimsJws(token); return true; } catch (Exception e) { // 处理异常 } return false; } } ``` #### 4. JWT 认证过滤器 (`JwtAuthenticationFilter.java`) ```java public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtils jwtUtils; @Autowired private UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String token = extractToken(request); if (token != null && jwtUtils.validateToken(token)) { String username = jwtUtils.getUsernameFromToken(token); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); } chain.doFilter(request, response); } } ``` #### 5. Redis 整合令牌黑名单 (`TokenBlacklistService.java`) ```java @Service public class TokenBlacklistService { @Autowired private RedisTemplate<String, String> redisTemplate; public void blacklistToken(String token) { // 存储到Redis并设置过期时间 redisTemplate.opsForValue().set(token, "blacklisted", Duration.ofMinutes(30)); } public boolean isBlacklisted(String token) { return Boolean.TRUE.equals(redisTemplate.hasKey(token)); } } ``` #### 6. 认证控制器 (`AuthController.java`) ```java @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtUtils jwtUtils; @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest request) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()) ); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtUtils.generateToken((UserDetails) authentication.getPrincipal()); return ResponseEntity.ok(new JwtResponse(jwt)); } } ``` #### 配置说明 1. **Redis配置**:在 `application.properties` 中添加: ```properties spring.redis.host=localhost spring.redis.port=6379 jwt.secret=yourSecretKey jwt.expiration=86400000 # 24小时 ``` 2. **安全流程**: - 用户登录 → 生成JWT令牌 - 后续请求携带令牌 → JWT过滤器验证令牌有效性 - Redis检查令牌是否在黑名单中 - Spring Security 建立安全上下文 #### 关键特性 1. **无状态认证**:使用JWT替代Session 2. **令牌吊销**:通过Redis黑名单实现即时令牌失效 3. **密码安全**:BCrypt加密存储密码 4. **CSRF防护**:禁用CSRF以适应无状态架构 [^1]: 引用自Spring Security官方文档的认证流程设计 [^2]: 参考JJWT库的令牌生成最佳实践
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值