Java Spring Boot中使用Shiro、JWT和Redis实现用户登录鉴权

  1. 引入依赖: 添加Shiro、JWT和Redis的依赖项。
  2. 配置Shiro: 配置Shiro的安全管理器、Realm等。
  3. 实现JWT工具类: 生成JWT Token、验证Token等功能。
  4. Controller层: 实现登录接口和受保护资源的访问接口。

首先,确保在pom.xml文件中添加以下依赖:

<!-- Shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.8.0</version> <!-- 替换为最新版本 -->
</dependency>

<!-- JWT -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version> <!-- 替换为最新版本 -->
</dependency>

<!-- Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

接下来,我们按照步骤逐步实现。

1. 配置Shiro

在Spring Boot的配置类中配置Shiro,主要是配置安全管理器、Realm等。

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/login"); // 登录URL

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置不需要权限认证的路径,比如登录接口
        filterChainDefinitionMap.put("/login", "anon");
        // 其他路径都需要认证
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilter;
    }

    @Bean
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    // 注册ShiroDialect,用于Thymeleaf中使用Shiro标签
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

2. 实现JWT工具类

编写JWT工具类来生成Token、验证Token,并结合Redis进行存储管理。

@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private long expiration;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    // 生成Token
    public String generateToken(String username) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);

        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    // 从Token中获取用户名
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    // 验证Token是否有效
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    // Token是否过期
    public boolean isTokenExpired(String token) {
        Date expiration = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getExpiration();
        return expiration.before(new Date());
    }

    // 保存Token到Redis
    public void saveTokenToRedis(String username, String token) {
        redisTemplate.opsForValue().set(username, token, expiration, TimeUnit.MILLISECONDS);
    }

    // 从Redis中删除Token
    public void deleteTokenFromRedis(String username) {
        redisTemplate.delete(username);
    }

    // 从Redis中获取Token
    public String getTokenFromRedis(String username) {
        return redisTemplate.opsForValue().get(username);
    }
}

3. Controller层

实现登录接口和需要认证的资源接口。

@RestController
public class AuthController {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @PostMapping("/login")
    public ResponseEntity<String> login(@RequestParam String username, @RequestParam String password) {
        // TODO: 根据用户名和密码验证用户身份,省略具体实现

        // 假设验证通过,生成Token并保存到Redis
        String token = jwtUtil.generateToken(username);
        jwtUtil.saveTokenToRedis(username, token);

        return ResponseEntity.ok(token);
    }

    @GetMapping("/protected/resource")
    public ResponseEntity<String> protectedResource(@RequestHeader("Authorization") String authorizationHeader) {
        String token = authorizationHeader.substring(7); // 去除Bearer

        // 验证Token是否有效,并从Token中获取用户名
        String username = jwtUtil.getUsernameFromToken(token);

        // TODO: 根据用户名获取受保护资源,省略具体实现

        return ResponseEntity.ok("Protected resource accessed by " + username);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值