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

yizhihongxing

下面我会详细讲解“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日

相关文章

  • 详解spring开发_JDBC操作MySQL数据库

    下面是“详解Spring开发_JDBC操作MySQL数据库”的完整攻略。 简介 本文将详细讲解如何使用Spring开发JDBC实现对MySQL数据库的操作。Spring JDBC封装了JDBC的操作,使得JDBC开发更加简单、方便。在本文中,我将介绍如何使用Spring JDBC实现数据库连接、数据源配置、CRUD操作等功能。 数据库连接配置 在使用Spri…

    Java 2023年5月19日
    00
  • idea之Recompile、Rebuild和Build之间的区别及说明

    在开发 Java 项目时,我们常会用到 IntelliJ IDEA 进行编码和项目构建。在 IDEA 的编译过程中,经常会遇到 Recompile、Rebuild 和 Build 这三个概念。这三个概念有何不同?下面我将为大家逐一解释其区别及说明。 什么是 Recompile? Recompile 意为“重新编译”,简单来说,就是重新编译单个 Java 文件…

    Java 2023年5月26日
    00
  • MyBatis逆向⼯程的生成过程

    下面我将为你详细讲解”MyBatis逆向工程的生成过程”的完整攻略。 1. 确定逆向工程生成的目标文件 逆向工程是根据数据库中的表自动生成基于MyBatis框架的Java代码。因此,在进行逆向工程之前,我们需要先确定逆向工程生成的目标文件,包括要使用哪个数据库、要生成哪些表的代码等。 2. 配置逆向工程的生成参数 在进行逆向工程之前,我们需要先配置生成参数。…

    Java 2023年5月20日
    00
  • 用JSP编写文件上传

    以下是使用JSP编写文件上传的完整攻略。 1. HTML表单 首先,我们需要在HTML文件中创建一个表单控件,让用户选择需要上传的文件并提交表单。代码如下: <form action="upload.jsp" method="post" enctype="multipart/form-data&quot…

    Java 2023年6月15日
    00
  • springboot配置Jackson返回统一默认值的实现示例

    下面是“springboot配置Jackson返回统一默认值的实现示例”的完整攻略。 1. 什么是Jackson Jackson是Java中主流的JSON解析库之一,用于Java对象和JSON数据之间的序列化和反序列化。在Spring Boot框架中,常用Jackson来将Java对象转换成JSON格式的数据,以便于前端页面对数据进行展示和处理。 2. 什么…

    Java 2023年5月26日
    00
  • 使用FileReader采用的默认编码

    使用FileReader对象默认采用的编码方式为UTF-8编码。但是,你也可以通过指定readAsText方法的第二个参数,来指定读取文件的编码方式。下面是使用FileReader对象进行文件读取的攻略: 步骤一:创建FileReader对象 在javascript中创建FileReader对象,可以使用下面的代码: var reader = new Fil…

    Java 2023年5月20日
    00
  • Java8 Stream 流常用方法合集

    Java8 Stream 流常用方法合集 Java 8 引入了一种新的抽象数据类型 Stream,它让数据的操作变得更加简单高效。Stream 可以是一组数据的集合、数组等等,它支持多方面的操作,比如过滤、映射、筛选、分组、去重、排序等等。下面将介绍 Java8 Stream 常用的方法。 创建流 从集合创建流:可以将一个集合转换为流,并对流中的元素进行操作…

    Java 2023年5月26日
    00
  • Java利用多线程模拟银行系统存钱问题

    Java利用多线程模拟银行系统存钱问题的完整攻略 1. 问题分析 假设有两个用户账户,分别在同一时间存钱,我们需要通过Java多线程模拟存钱的过程并确保数据的准确性和安全性。 2. 解决方案 为了确保数据的安全,Java使用了synchronized关键字来实现线程同步,同时也使用了wait()和notify()方法来解决线程的等待和调度问题。 Java中可…

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