SpringSceurity(7)--- Security+JWT搭建简单的授权服务器和资源服务器

本文通过Spring Security结合JWT,详细介绍了如何搭建授权服务器和资源服务器。流程包括设置用户名和密码,配置两个服务器的端口号,并进行测试。在测试部分,通过Postman展示了获取JWT令牌并使用该令牌访问资源服务器的过程,验证了系统的正确性。注意,此示例仅作为基础演示,数据未动态配置,配置类细节未展开讨论。

之前有两篇博客分别将来介绍了 OAuth 2.0 和 JWT

1、OAuth 2.0详解

2、SpringSceurity(6)---JWT详解

这边通过Security+JWT搭建简单的授权服务器和资源服务器示例。

整体流程大概是这样的

JWT 认证流程

1、用户先访问授权服务器。传入用户输入用户名/密码登录,授权服务器认证成功后,会返回给客户端一个 JWT
2、用户在访问资源服务器。当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样
   Authorization: Bearer复制代码
3、资源服务器保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为

下面开始搭建

一、搭建授权服务器

授权服务器配置类

/**
 * @Description: 授权服务器配置
 *   使用 
<think>嗯,用户想要用Spring Security Web、JWTRedis来搭建用户登录模块,需要完整的代码。首先,我得确认用户的需求。他们可能是在开发一个需要用户认证的Web应用,希望实现基于Token的无状态认证,同时利用Redis管理Token的有效性。可能的应用场景包括分布式系统或者需要横向扩展的服务,需要确保用户登录状态的高效管理。 首先,我应该考虑整个架构的组成部分。用户模块通常包括用户注册、登录、登出,以及权限控制。使用Spring Security作为安全框架,JWT用于生成验证Token,Redis用来存储有效的Token或者黑名单,以增强安全性,比如实现Token的主动失效。 接下来,需要确定各个组件之间的交互流程。用户登录时,系统验证用户名密码,生成JWT并存入Redis,返回给客户端。之后的请求需要携带JWTSpring Security的过滤器会验证Token的有效性,并检查Redis中是否存在,以确定是否允许访问。登出时,将Token加入黑名单,防止继续使用。 然后,考虑各个技术点的实现细节。比如,如何集成Spring Security的配置,自定义UserDetailsService来从数据库加载用户,JWT工具类的编写,以及Redis的操作。需要处理密码加密,使用BCryptPasswordEncoder。还要处理异常情况,比如Token过期、无效等情况,返回相应的错误信息。 可能遇到的问题包括Spring Security的过滤器链配置,确保JWT过滤器在合适的位置;Token的生成验证过程中的签名算法是否正确;Redis的键值设计,比如存储Token时使用用户ID作为键的一部分,方便管理清除。另外,需要考虑Token的刷新机制,但用户可能暂时不需要,所以暂时可以不实现。 现在,需要分步骤构建代码结构。首先,项目的依赖管理,比如pom.xml中添加Spring SecurityJWT、Redis相关的依赖。然后,配置Spring Security,禁用默认的Session管理,配置密码编码器,设置权限规则。编写JWT过滤器,继承OncePerRequestFilter,在请求头中提取Token并进行验证。验证通过后,将用户信息存入SecurityContextHolder。 用户实体类Repository需要与数据库交互,这里可能需要使用Spring Data JPA。自定义UserDetailsService实现,从数据库加载用户信息。然后,编写认证逻辑,在登录接口中调用AuthenticationManager的authenticate方法,生成Token并保存到Redis。 在控制器层,需要处理登录、登出、测试接口。登出接口需要将当前Token加入Redis黑名单,并在后续请求中检查黑名单中的Token是否有效。 还需要配置RedisTemplate,确保能够正确操作Redis中的数据。异常处理方面,使用Spring的全局异常处理来捕获认证授权相关的异常,返回统一的响应格式。 测试时需要注意的事项包括正确设置请求头中的Authorization字段,携带Bearer Token。登出后,该Token应无法再次使用。另外,Redis中存储的Token需要有合理的过期时间,与JWT的过期时间一致或更长,避免Token失效后Redis中仍有残留。 可能还需要考虑安全性问题,比如HTTPS的使用,Token的存储位置(如HttpOnly的Cookie),但用户的问题主要集中在功能实现上,这些高级安全措施可能暂时不需要深入。 最后,确保代码结构清晰,模块分明,各部分职责明确。提供完整的代码示例,帮助用户理解每个部分的作用,并能够根据自身需求进行调整。</think>以下是一个基于Spring Security + JWT + Redis的完整登录模块实现方案,包含关键代码配置说明: --- ### 一、项目结构 ``` src/main/java ├── com.example.demo │ ├── config │ │ ├── SecurityConfig.java │ │ └── RedisConfig.java │ ├── controller │ │ └── AuthController.java │ ├── filter │ │ └── JwtAuthenticationFilter.java │ ├── pojo │ │ ├── dto │ │ │ ├── LoginDTO.java │ │ │ └── ResponseResult.java │ │ ├── entity │ │ │ └── User.java │ ├── repository │ │ └── UserRepository.java │ ├── service │ │ ├── impl │ │ │ └── UserDetailsServiceImpl.java │ │ └── LoginService.java │ ├── utils │ │ ├── JwtUtil.java │ │ └── RedisCache.java │ └── DemoApplication.java ``` --- ### 二、核心代码实现 #### 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</artifactId> <version>0.9.1</version> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 其他必要依赖... --> </dependencies> ``` #### 2. Security配置类 ```java @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/user/login").anonymous() .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } } ``` #### 3. JWT工具类 ```java @Component public class JwtUtil { private static final String SECRET_KEY = "your-secret-key"; private static final long EXPIRATION = 86400000; // 24小时 public String generateToken(String username) { return Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public String getUsernameFromToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody() .getSubject(); } } ``` #### 4. Redis工具类 ```java @Component public class RedisCache { @Autowired private RedisTemplate<String, Object> redisTemplate; public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } public Object get(String key) { return redisTemplate.opsForValue().get(key); } public Boolean delete(String key) { return redisTemplate.delete(key); } } ``` #### 5. JWT认证过滤器 ```java public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Autowired private RedisCache redisCache; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = request.getHeader("Authorization"); if (StringUtils.hasText(token) && token.startsWith("Bearer ")) { token = token.substring(7); try { String username = jwtUtil.getUsernameFromToken(token); // 检查Redis中是否存在有效token String redisKey = "login:" + username; String redisToken = (String) redisCache.get(redisKey); if (redisToken != null && redisToken.equals(token)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>()); SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { // Token验证失败处理 } } filterChain.doFilter(request, response); } } ``` #### 6. 登录接口实现 ```java @RestController public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtUtil jwtUtil; @Autowired private RedisCache redisCache; @PostMapping("/user/login") public ResponseResult<String> login(@RequestBody LoginDTO loginDTO) { // 执行认证 Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginDTO.getUsername(), loginDTO.getPassword() ) ); // 生成Token String username = authentication.getName(); String token = jwtUtil.generateToken(username); // 存储到Redis String redisKey = "login:" + username; redisCache.set(redisKey, token); return ResponseResult.success(token); } } ``` --- ### 三、关键配置说明 1. **Redis配置**: ```java @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } } ``` 2. **用户认证服务**: ```java @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("用户不存在"); } return new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), new ArrayList<>() ); } } ``` --- ### 四、功能流程 1. **登录流程**: ``` 客户端请求 -> 校验用户名密码 -> 生成JWT -> 存入Redis -> 返回Token给客户端 ``` 2. **请求鉴权流程**: ``` 客户端携带Token -> 过滤器验证Token有效性 -> 检查Redis是否存在 -> 设置SecurityContext -> 访问受保护资源 ``` 3. **登出实现**: ```java @PostMapping("/user/logout") public ResponseResult<Void> logout() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String username = authentication.getName(); redisCache.delete("login:" + username); return ResponseResult.success(); } ``` --- ### 五、注意事项 1. 需要配置`application.yml`中的Redis连接信息 2. 生产环境需要配置HTTPS 3. 建议使用更复杂的密钥管理方案 4. 可添加Token刷新机制 5. 需要实现用户角色权限控制时可扩展`UserDetails` 以上实现方案可根据具体业务需求进行扩展调整,建议结合Spring Boot Actuator进行健康监控。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值