【问题标题】:JSON Web Token in spring session春季会议中的 JSON Web 令牌
【发布时间】:2022-01-27 09:51:57
【问题描述】:

我有三台生产服务器和一个全局负载均衡器,当第一个请求到来时,请求得到验证并生成 jwt 令牌,并且令牌存储在服务器的 spring 会话中,但是当新请求到来时,它转到了不同的服务器让我登出。

我应该在这里利用什么我阅读了一些关于在 Redis 中存储令牌的内容,但我不了解流程如何准确使用它,或者有什么不同。我在这里有什么选择吗?

在 Dev 中,我只有一台服务器,因此无法对其进行测试。

谁能给我一些实际的例子来解决这个问题

【问题讨论】:

  • 您好,欢迎来到社区!如果您没有在集群环境中使用由数据库支持的 Spring Session,这是预期的,因为会话绑定在应用程序容器上。服务器必须有一个真实的来源来检查给定主体的会话是否存在。这个baeldung.com/spring-session-jdbc 将向您展示配置Spring Session JDBC 或这个javainuse.com/spring/springboot_session_redis 用于Redis 实现是多么容易。最后,您可以通过在不同端口上本地运行您的应用程序来模拟您的应用程序的 N 个实例来测试这一点。
  • @junbetterway我可以使用cookies来实现这一点,就像创建一个cookie一样,我们需要将它发送给客户端作为响应,这样才能解决问题。
  • Spring Session 带有一个使用 DefaultCookieSerializer 的默认实现,您可以通过以下链接进一步自定义它:docs.spring.io/spring-session/reference/…

标签: java spring-boot redis jwt


【解决方案1】:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private JWTRequestFilter jwtRequestFilter;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        ...
        }
    }

JWT Util 文件,包含所有与 JWT 验证和生成相关的功能

@Component
public class JWTUtil {

    public static final long EXPIRATION_TIME = 60 * 60 * 24;
    public static final String TOKEN_PREFIX = "Bearer ";
    public static final String HEADER_STRING = "Authorization";

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

    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(secret.getBytes()).build().parseClaimsJws(token).getBody();
    }

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return doGenerateToken(claims, userDetails.getUsername());
    }

    private String doGenerateToken(Map<String, Object> claims, String username) {
        SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
        return Jwts.builder().setClaims(claims)
                .setSubject(username).setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME * 1000))
                .signWith(key).compact();
    }

    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    public boolean validateToken(String token, String requestUsername) {
        final String username = getUsernameFromToken(token);
        return (username.equals(requestUsername) && !isTokenExpired(token));
    }
}

Jwt if 过滤文件的重要组成部分,包含要过滤的 URL

@Service
public class JWTRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JWTUtil jwtUtil;

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    Set<String> urls = new HashSet<String>(Arrays.asList("/api/login", "/api/register", "/api/"));

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        String path = request.getRequestURI();
        return urls.contains(path);
    }

    protected boolean shouldNotFilterErrorDispatch() {
        return true;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String jwtToken = null;
        String username = null;

        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals("Authorization")) {
                jwtToken = cookie.getValue();
            }
        }

        if (jwtToken != null) {
            try {
                username = jwtUtil.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                System.out.println("JWT Token has expired");
            }
        } else {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        }

        if (username != null) {
            UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username);
            if (!jwtUtil.validateToken(jwtToken, userDetails.getUsername())) {
                logger.warn("JWT Token validation failed");
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            }
        }
        doFilter(request, response, filterChain);
    }
} 

【讨论】:

    猜你喜欢
    • 2016-11-04
    • 1970-01-01
    • 2018-11-30
    • 2015-04-11
    • 2011-12-31
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多