token被截获,如何防止

问题 :用security+jwt 方案进行登录认证。那么登录成功后,生成的token如何防止被窃取

  • 帮你找了个相似的问题, 你可以看下: https://ask.csdn.net/questions/7689696
  • 这篇博客你也可以参考下:第七章:使用jwt token的方式来进行登录
  • 除此之外, 这篇博客: Security之整合JWT篇中的 4、第一阶段,登录认证,获取token 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 首次登录,还是以传统的表单登录模式,所以我们可以直接使用UsernamePasswordAuthenticationFilter来拦截/login进行认证处理,不过我这里自定义了一个JwtAuthenticationFilter,其功能跟UsernamePasswordAuthenticationFilter差不多
    JwtAuthenticationFilter 继承 UsernamePasswordAuthenticationFilter 并重写 attemptAuthentication

    package com.yzm.security08.config;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.AuthenticationServiceException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    
        public JwtAuthenticationFilter() {
            super();
        }
    
        public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
            super(authenticationManager);
        }
    
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            String username = obtainUsername(request);
            String password = obtainPassword(request);
            if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
                throw new AuthenticationServiceException("用户名密码错误");
            }
    		// 构造未鉴权的JwtAuthenticationToken 对象
            JwtAuthenticationToken authToken = new JwtAuthenticationToken(username, password);
            this.setDetails(request, authToken);
            return this.getAuthenticationManager().authenticate(authToken);
        }
    
    }
    

    JwtAuthenticationToken 继承 UsernamePasswordAuthenticationToken

    package com.yzm.security08.config;
    
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.GrantedAuthority;
    
    import java.util.Collection;
    
    public class JwtAuthenticationToken extends UsernamePasswordAuthenticationToken {
    
        public JwtAuthenticationToken(Object principal, Object credentials) {
            super(principal, credentials);
        }
    
        public JwtAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
            super(principal, credentials, authorities);
        }
    }
    

    身份认证 Provider,获取UserDetails对象,校验密码,构造鉴权的Authentication对象

    package com.yzm.security08.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Slf4j
    public class JwtAuthenticationProvider implements AuthenticationProvider {
    
        private final UserDetailsService userDetailsService;
        private final PasswordEncoder passwordEncoder;
    
        public JwtAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
            this.userDetailsService = userDetailsService;
            this.passwordEncoder = passwordEncoder;
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            return JwtAuthenticationToken.class.isAssignableFrom(authentication);
        }
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            JwtAuthenticationToken authenticationToken = (JwtAuthenticationToken) authentication;
            String username = (String) authenticationToken.getPrincipal();
            String password = (String) authenticationToken.getCredentials();
    
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (userDetails == null) throw new UsernameNotFoundException("账号异常");
    
            if (!passwordEncoder.matches(password, userDetails.getPassword())) {
                throw new BadCredentialsException("凭证异常");
            }
    
            // 构造鉴权的JwtAuthenticationToken对象
            JwtAuthenticationToken resultToken = new JwtAuthenticationToken(username, password,
                    userDetails.getAuthorities());
            resultToken.setDetails(authentication.getDetails());
            return resultToken;
        }
    }
    

    登录认证成功之后,返回token

    package com.yzm.security08.config;
    
    import com.yzm.common.utils.HttpUtils;
    import com.yzm.security08.utils.JwtUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    @Slf4j
    public class JwtAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
            if (authentication instanceof JwtAuthenticationToken) {
                log.info("登录认证");
                JwtAuthenticationToken authenticationToken = (JwtAuthenticationToken) authentication;
                String username = (String) authenticationToken.getPrincipal();
    
                Map<String, Object> map = new HashMap<>();
                map.put(JwtUtils.USERNAME, username);
                String token = JwtUtils.generateToken(map);
                HttpUtils.successWrite(response, token);
            }
        }
    }
    
  • 您还可以看一下 崔海焘老师的新版前后端接口安全技术JWT+RSA加密课程中的 保证token一直有效-刷新token小节, 巩固相关知识点
  • 以下回答由chatgpt基于相关博客总结生成:

    为了防止他人截获或盗用登录令牌(token),可以采取以下措施:

    1. 使用https协议传输数据,以确保通信过程中的加密安全性。

    2. 设置token过期时间,限制token的有效时间。

    3. 采用密钥对token进行签名,防止他人伪造。密钥只在服务器端存储,不要泄漏。

    4. 不在代码中明文显示密钥,可以采用将密钥写在配置文件中,然后读取配置文件获取密钥。

    5. 不能将token存储在客户端的cookie和localStorage中,避免被恶意攻击者获取。

    6. 需要对请求头进行验证,只有携带正确的token且未过期才能进行相关操作。

    可以参考下面的代码:

    // 配置JWT生成工具类
    public class JwtTokenUtils {
    
        // 签名密钥,应与服务器端的密钥相同,保持安全
        private static final String secret = "yourSecretKey";
    
        // 过期时间,这里设置为2小时
        private static final long expireTime = 2 * 60 * 60 * 1000;
    
        /**
         * 生成token
         */
        public static String generateToken(String username, List<String> roles) {
            Date now = new Date();
            Date expireDate = new Date(now.getTime() + expireTime);
            Map<String, Object> claims = new HashMap<>();
            claims.put("username", username);
            claims.put("roles", roles);
            return Jwts.builder()
                    .setClaims(claims)
                    .setIssuedAt(now)
                    .setExpiration(expireDate)
                    .signWith(SignatureAlgorithm.HS256, secret)
                    .compact();
        }
    
        /**
         * 验证token是否有效
         */
        public static boolean validateToken(String token) {
            try {
                Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
                return true;
            } catch (Exception ex) {
                return false;
            }
        }
    
        /**
         * 从token中获取用户名
         */
        public static String getUsername(String token) {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return claims.get("username", String.class);
        }
    
        /**
         * 从token中获取用户角色
         */
        public static List<String> getRoles(String token) {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            List<String> roles = (List<String>) claims.get("roles", List.class);
            return roles;
        }
    }
    
    // 配置JWT的过滤器
    public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    
        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            String token = httpServletRequest.getHeader("Authorization");
            if (token != null && token.startsWith("Bearer ")) {
                token = token.substring(7);
            }
            if (token != null && JwtTokenUtils.validateToken(token)) {
                String username = JwtTokenUtils.getUsername(token);
                List<String> roles = JwtTokenUtils.getRoles(token);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }
    
    // 配置权限管理器
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                .addFilterAfter(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/api/authenticate").permitAll()
                .anyRequest().authenticated();
        }
    
        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(loginUserDetailsService);
        }
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    }