spring security结合jwt实现用户重复登录处理

下面我会详细讲解“spring security结合jwt实现用户重复登录处理”的完整攻略。

概述

在使用JWT(Json Web Token)作为身份认证的情况下,用户可以随时提供令牌来访问应用程序,这使得应用程序无法管理用户的会话状态,例如强制注销用户或在重复登录情况下限制访问。为了解决这个问题,我们可以使用Spring Security来管理用户登录状态。本文将描述如何使用Spring Security和JWT结合实现用户重复登录的控制。

步骤

以下是实现用户重复登录处理的步骤:

步骤1: 添加依赖关系

首先,我们需要添加下面两个依赖关系:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>${spring-security.version}</version>
</dependency>

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jjwt.version}</version>
</dependency>

步骤2: 创建JWT工具类

我们需要一个JWT工具类来创建和解析JWT令牌。以下是示例代码:

@Component
public class JwtTokenUtil {
    private final String secret = "mySecret";

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder().setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
                .signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    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());
    }

    public Date getExpirationDateFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getExpiration();
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
    }
}

步骤3: 创建一个JWT过滤器

我们需要一个JWT过滤器来解析JWT令牌并将其绑定到Spring Security上下文中。以下是示例代码:

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserDetailsServiceImpl userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String authToken = request.getHeader("Authorization");
        String username = jwtTokenUtil.getUsernameFromToken(authToken);
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userService.loadUserByUsername(username);
            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }
}

步骤4: 创建一个JWT登录策略

我们需要一个JWT登录策略来定义如何响应登录和注销请求。以下是示例代码:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsServiceImpl userService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

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

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .csrf().disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().logout().logoutSuccessHandler((request, response, authentication) -> response.setStatus(HttpServletResponse.SC_OK))
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true).permitAll()
                .and().exceptionHandling().authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"))
                .and().sessionManagement().maximumSessions(1).expiredUrl("/sessionExpired");
    }

    @Bean
    public JwtTokenUtil jwtTokenUtil() {
        return new JwtTokenUtil();
    }
}

步骤5: 创建用户服务

我们需要一个用户服务来加载用户详细信息。以下是示例代码:

@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("User not found with username: " + username);
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
    }
}

步骤6: 创建示例Controller

我们需要一个示例Controller来测试用户重复登录控制的效果。以下是示例代码:

@RestController
public class AuthController {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody User user) throws Exception {
        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
        } catch (DisabledException e) {
            throw new Exception("USER_DISABLED", e);
        } catch (BadCredentialsException e) {
            throw new Exception("INVALID_CREDENTIALS", e);
        }
        final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
        final String token = jwtTokenUtil.generateToken(userDetails);
        return ResponseEntity.ok(new AuthToken(token));
    }

    @PostMapping("/logout")
    public ResponseEntity<?> logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        SecurityContextHolder.clearContext();
        if (session != null) {
            session.invalidate();
        }
        return ResponseEntity.ok().body("Logged out successfully");
    }
}

class AuthToken {
    private String token;

    public AuthToken(String token) {
        this.token = token;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}

以上就是使用Spring Security和JWT结合实现用户重复登录处理的完整攻略。使用上述步骤,我们可以实现对用户登录状态的完整管理,实现更加安全的用户认证和会话管理。

阅读剩余 77%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:spring security结合jwt实现用户重复登录处理 - Python技术站

(0)
上一篇 2023年6月3日
下一篇 2023年6月3日

相关文章

  • 类加载的生命周期包括哪些阶段?

    以下是关于类加载的生命周期包括哪些阶段的详细讲解: 类加载的生命周期包括哪些阶段? 类加载的生命周期包括以下几个阶段: 加载(Loading):将类的字码加载到内存中。 链接(Linking):将类的二进制数据合并到 Java 运行时环境中。 验证(Verification):验证的字节码是否符合 Java 虚拟机规范。 准备(Preparation):为类…

    Java 2023年5月12日
    00
  • Sprint Boot @ConditionalOnClass使用方法详解

    @ConditionalOnClass是Spring Boot中的一个注解,它用于根据类路径中是否存在指定的类来决定是否启用或禁用某个组件。在使用Spring Boot开应用程序时,@ConditionalOnClass是非常有用的。本文将详细介绍@ConditionalOnClass的作用和使用方法,并提供两个示例说明。 @ConditionalOnCla…

    Java 2023年5月5日
    00
  • Java数组常见应用详解【创建、遍历、排序、查找】

    Java数组常见应用详解 数组是一种非常常见的数据结构,它可以用于存储一组数据,并且支持快速的遍历、排序和查找等操作。在Java中,数组是一个容器对象,可以存储相同类型的元素,并且在创建后其大小是不可改变的。本文将详细介绍Java数组的创建、遍历、排序和查找等常见应用,让大家对Java数组有更深入的了解。 创建数组 在Java中,可以通过以下方式来创建数组:…

    Java 2023年5月26日
    00
  • Java 配置加载机制详解及实例

    Java 配置加载机制详解及实例 在 Java 中,配置文件被广泛用于存储应用程序的配置信息。应用程序在启动时需要读取配置文件并使用其中的参数。如果你使用 Java 编写应用程序,你需要掌握 Java 中的配置文件的加载机制。 配置文件的加载机制 Java 中的配置文件可以使用多种格式,如 .properties、.xml、.json 等。在加载配置文件时,…

    Java 2023年6月2日
    00
  • SpringBoot @PostMapping接收HTTP请求的流数据问题

    要实现SpringBoot @PostMapping接收HTTP请求的流数据,需要遵循以下步骤: 在Controller中添加接口,使用@PostMapping注解,并使用@RequestBody注解请求参数; 创建一个封装流数据的Java对象,并使用@RequestBody注解接收请求参数; 在请求头中添加Content-Type字段,值为applicat…

    Java 2023年6月3日
    00
  • Java编程基础元素-运算符

    Java编程基础元素-运算符 介绍 在Java编程中,运算符是用于对数据进行操作的一种符号或关键字。Java编程语言支持以下类型的运算符: 算术运算符 关系运算符 位运算符 逻辑运算符 条件运算符 赋值运算符 这些运算符可以应用于不同的数据类型,例如整数、字符、浮点数、布尔值等。 算术运算符 算术运算符用于执行基本的算术操作,例如加、减、乘、除和取模运算。J…

    Java 2023年5月26日
    00
  • 使用weixin-java-miniapp配置进行单个小程序的配置详解

    使用weixin-java-miniapp配置进行单个小程序的配置,需要遵循下面的步骤: 1. 引入依赖 在pom.xml文件中引入以下依赖: <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-miniap…

    Java 2023年5月23日
    00
  • Java8简单了解Lambda表达式与函数式接口

    Java8简单了解Lambda表达式与函数式接口攻略 什么是Lambda表达式? Lambda表达式是一种匿名函数,可以看成是对匿名类的一种简化写法,它能够以更简洁的语法实现相同的功能。 Lambda表达式的语法格式如下: (parameters) -> expression 其中,参数可以有0个或多个,参数类型可以显式声明,也可以根据上下文自动推断;…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部