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结合实现用户重复登录处理的完整攻略。使用上述步骤,我们可以实现对用户登录状态的完整管理,实现更加安全的用户认证和会话管理。

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

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

相关文章

  • Java 实现网络爬虫框架详细代码

    我将为您详细讲解Java 实现网络爬虫框架的攻略。 什么是网络爬虫 网络爬虫(英语:web crawler),也叫做网络蜘蛛(spider),是一种按照一定的规则和算法,自动访问万维网信息的程序或脚本。网络爬虫可以从互联网上自动获取信息,并通过许多处理方法对这些信息进行重组和筛选,从而给用户提供全面和高效的信息检索服务。 网络爬虫的实现 在Java中,我们可…

    Java 2023年5月19日
    00
  • Spring 代码技巧梳理总结让你爱不释手

    Spring 代码技巧梳理总结让你爱不释手攻略 介绍 Spring是一款开源的,轻量级的Java开发框架。它包含了一系列的工具,使得Java开发更加容易和高效。在本攻略中,我们会总结出一些Spring开发中的常用技巧,以帮助你更加熟悉和熟练地使用Spring。 技巧列表 使用@Autowired简化依赖注入 在Spring中,我们可以使用@Autowired…

    Java 2023年5月19日
    00
  • Java基本数据类型和运算符详解

    Java基本数据类型和运算符详解 在Java中,有8种基本数据类型,它们分别为:byte、short、int、long、float、double、char、boolean。 接下来的攻略会详细阐述每种基本数据类型的含义和使用,以及Java的运算符使用方法。 八种基本数据类型 byte(8位) byte类型用于存储字节型数据,它占用8个二进制位,取值范围为-1…

    Java 2023年5月26日
    00
  • MyBatis如何实现流式查询的示例代码

    流式查询是MyBatis中常用的一种查询方式,能够在处理大量数据时提高查询效率。以下是详细的 MyBatis 如何实现流式查询的攻略,包括两条示例代码: 1. 流式查询 流式查询被称为“游标”查询,是基于 JDBC 游标实现的。它的实现方式是通过一次读取一批数据,然后处理它们,最后再继续读取下一批数据。这样可以避免一次性读取所有匹配数据所带来的内存开销和响应…

    Java 2023年5月19日
    00
  • 详解使用canvas保存网页为pdf文件支持跨域

    详解使用canvas保存网页为PDF文件支持跨域的完整攻略。 1. 简介 现在越来越多的网站需要支持生成PDF文件。而通过canvas来保存HTML页面为PDF文件是非常流行的一种解决方案,同时它也支持跨域。 2. 实现过程 2.1 引入jsPDF库 我们会使用到一个叫做jsPDF的库来实现将HTML页面转为PDF文件的操作。所以我们首先需要在HTML页面中…

    Java 2023年6月16日
    00
  • 详解CentOS 7下安装Tomcat到服务

    下面是详解CentOS 7下安装Tomcat到服务的完整攻略,分为以下步骤: 步骤一:安装Java环境 在CentOS 7下安装Tomcat之前,需要先安装Java环境,这里我们使用OpenJDK: 更新软件包: sudo yum update 安装OpenJDK: sudo yum install java-1.8.0-openjdk-devel 验证Ja…

    Java 2023年5月19日
    00
  • Spring Boot集成Mybatis的实例代码(简洁版)

    Spring Boot 集成 MyBatis 的完整攻略 Spring Boot 是一个快速构建 Spring 应用程序的框架,它提供了许多便利的功能,例如自动配置、嵌入式服务器和健康检查等。在本文中,我们将详细讲解 Spring Boot 集成 MyBatis 的完整攻略。 步骤一:创建 Spring Boot 项目 首先,我们需要创建一个 Spring …

    Java 2023年5月15日
    00
  • 几则JSP入门知识总结

    下面我将详细讲解“几则JSP入门知识总结”的完整攻略。 什么是JSP? JSP全称为JavaServer Pages,它是一种HTML页面开发的技术标准,它允许Java代码和一些特殊的JSP标记被嵌入到HTML页面中。JSP旨在简化动态网页的创建,它可以很容易地与Java Servlets集成。 JSP基础知识 JSP文件结构 在JSP中,我们可以将Java…

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