利用JWT Token配合Redis实现单点登录并进行Token的缓存验证唯一性

实现单点登录(Single Sign-On, SSO)需要以下步骤:

  1. 用户登录后,生成JWT Token并将其存储到Redis中。
  2. 将JWT Token返回给客户端,并在响应头中设置Authorization字段,值为Bearer加上JWT Token。
  3. 客户端在每次请求时,在请求头中带上Authorization字段,值为Bearer加上JWT Token。
  4. 服务端在接收到请求时,先从请求头中获取JWT Token,并解析出其中的用户信息。然后再到Redis中验证Token的唯一性和有效期。
  5. 如果验证通过,则允许用户访问资源;否则拒绝访问。
  • 首先,pom.xml文件中引入以下依赖
  • <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.2</version>
    </dependency>
    
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.2</version>
        <scope>runtime</scope>
    </dependency>
    
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.2</version>
        <scope>runtime</scope>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    

    然后,在application.yml文件中配置JWT Token的相关属性(secret为加密密钥,expiration为有效期):

    jwt:
      secret: mysecretkey
      expiration: 600
    

  • 然后,创建一个JwtUtil类,用于生成和解析JWT Token:

  • import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import io.jsonwebtoken.security.Keys;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import javax.crypto.SecretKey;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    public class JwtUtil {
        @Value("${jwt.secret}")
        private String secret;
    
        @Value("${jwt.expiration}")
        private Long expiration;
    
        private SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
    
        public String generateToken(String username) {
            Date now = new Date();
            Date expiryDate = new Date(now.getTime() + expiration * 1000);
    
            Map<String, Object> claims = new HashMap<>();
            claims.put("sub", username);
            claims.put("iat", now);
            claims.put("exp", expiryDate);
    
            return Jwts.builder()
                    .setClaims(claims)
                    .signWith(key, SignatureAlgorithm.HS512)
                    .compact();
        }
    
        public String getUsernameFromToken(String token) {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
    
            return claims.getSubject();
        }
        
        public Long getExpiration() {
            return expiration;
        }
    }
    

    创建一个JwtFilter类,用于验证JWT Token的唯一性和有效性:

    import com.alibaba.fastjson.JSONObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.concurrent.TimeUnit;
    
    @Component
    public class JwtFilter extends OncePerRequestFilter {
        @Autowired
        private JwtUtil jwtUtil;
    
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            String authToken = httpServletRequest.getHeader("Authorization");
            if (StringUtils.hasText(authToken) && authToken.startsWith("Bearer ")) {
                authToken = authToken.substring(7);
    
                String username = jwtUtil.getUsernameFromToken(authToken);
                Object tokenInRedis = redisTemplate.opsForValue().get(username);
                if (tokenInRedis == null || !tokenInRedis.equals(authToken)) {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
                    JSONObject result = new JSONObject();
                    result.put("status", HttpStatus.UNAUTHORIZED.value());
                    result.put("message", "Token验证失败或已过期,请重新登录!");
                    httpServletResponse.getWriter().write(result.toString());
    
                    return;
                }
    
    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, null);
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    
                redisTemplate.opsForValue().set(username, authToken, jwtUtil.getExpiration(), TimeUnit.SECONDS);
            }
        }
    
    
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }
    

    在登录控制器中生成JWT Token并保存到Redis中:

    @RestController
    public class LoginController {
        @Autowired
        private JwtUtil jwtUtil;
    
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
        @PostMapping("/login")
        public String login(@RequestParam String username, @RequestParam String password) {
            // TODO: 用户名和密码验证
            String token = jwtUtil.generateToken(username);
            redisTemplate.opsForValue().set(username, token, jwtUtil.getExpiration(), TimeUnit.SECONDS);
    
            return token;
        }
    }
    

    最后,在Spring Security的配置类中添加JWT Token的过滤器:

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private JwtFilter jwtFilter;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }
    

    登录控制层

    
    UserController类:
    
    ```java
    import cn.chatmind.demo.mapper.UserMapper;
    import cn.chatmind.demo.model.User;
    import com.alibaba.fastjson.JSONObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/api")
    public class UserController {
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private JwtUtil jwtUtil;
    
        @PostMapping("/login")
        public JSONObject login(@RequestBody User user) {
            JSONObject responseJson = new JSONObject();
    
            User existUser = userMapper.findByUsernameAndPassword(user);
    
            if (existUser != null) {
                String token = jwtUtil.generateToken(existUser.getUsername());
                responseJson.put("token", token);
    
                return responseJson;
            } else {
                responseJson.put("error", "Bad Request");
                responseJson.put("message", "Invalid username or password.");
                return responseJson;
            }
        }
    
        @GetMapping("/users/{id}")
        public User getUserById(@PathVariable("id") Integer id) {
            return userMapper.findUserById(id);
        }
    }
    

    以上为一个基础的使用Spring Boot、MyBatis、Redis和JWT实现用户登录认证的完整示例。具体实现中可以根据需求进行更改和优化。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值