Java微服务项目:家具商城同时拥有PC端、小程序、APP,实现单点登录功能,使用Spring Security+JWT+Redis完成单点登录,代码实现流程
时间: 2024-04-06 14:34:35 浏览: 196
好的,针对你的问题,我会给出一份 Java 微服务项目:家具商城的单点登录功能实现代码流程,使用 Spring Security+JWT+Redis 完成单点登录。代码实现流程如下:
1. 添加依赖
在 pom.xml 文件中添加以下依赖:
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```
2. 配置 Spring Security
在 Spring Security 配置类中,添加以下配置:
```
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
```
3. 实现 JWT 工具类
实现 JwtTokenUtil 工具类,用于生成和解析 JWT Token。
```
@Component
public class JwtTokenUtil {
private static final String SECRET = "secret";
private static final String ISSUER = "issuer";
private static final String AUDIENCE = "audience";
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
private static final long EXPIRATION_TIME = 86400000; // 24 hours
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return Jwts.builder()
.setClaims(claims)
.setIssuer(ISSUER)
.setAudience(AUDIENCE)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public String getUsernameFromToken(String token) {
String username;
try {
final Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
public Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
public boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
}
```
4. 实现认证过滤器
实现 JwtAuthenticationTokenFilter 过滤器,用于在请求头中解析 JWT Token,并进行认证。
```
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String authToken = authHeader.substring("Bearer ".length());
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
chain.doFilter(request, response);
}
}
```
5. 实现单点登录功能
在登录接口中,使用 JWT 工具类生成 JWT Token,并将 Token 存储到 Redis 中,设置 Token 过期时间,并返回 Token 给客户端。
```
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private UserService userService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
User user = userService.findByUsername(loginRequest.getUsername());
if (user == null) {
return ResponseEntity.badRequest().body(new ApiResponse(false, "Invalid username"));
}
if (!user.getPassword().equals(loginRequest.getPassword())) {
return ResponseEntity.badRequest().body(new ApiResponse(false, "Invalid password"));
}
UserDetails userDetails = new User(user.getUsername(), user.getPassword(), new ArrayList<>());
String token = jwtTokenUtil.generateToken(userDetails);
redisTemplate.opsForValue().set(token, userDetails.getUsername(), jwtTokenUtil.EXPIRATION_TIME, TimeUnit.MILLISECONDS);
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
}
```
6. 实现单点登录拦截器
在请求过程中,使用 JwtAuthenticationInterceptor 拦截器,从请求头中解析 JWT Token,并从 Redis 中获取 Token 对应的用户名,将用户名设置到请求属性中,供后续业务使用。
```
@Component
public class JwtAuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String authToken = authHeader.substring("Bearer ".length());
String username = (String) redisTemplate.opsForValue().get(authToken);
if (username != null && !jwtTokenUtil.isTokenExpired(authToken)) {
request.setAttribute("username", username);
return true;
}
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
```
7. 配置 Redis
在 application.properties 中,添加以下配置:
```
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
```
至此,Java 微服务项目:家具商城的单点登录功能使用 Spring Security+JWT+Redis 完成。
阅读全文