- 引入依赖: 添加Shiro、JWT和Redis的依赖项。
- 配置Shiro: 配置Shiro的安全管理器、Realm等。
- 实现JWT工具类: 生成JWT Token、验证Token等功能。
- 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); } }