SpringBoot整合SpringSecurity实现JWT认证的项目实践

下面就给您详细讲解“SpringBoot整合SpringSecurity实现JWT认证的项目实践”的完整攻略。

一、什么是JWT

JWT(JSON Web Token)是一种用于对信息进行安全传输的开放标准,它将信息进行编码后生成一段字符串,用于在不同业务系统之间传递信息。在进行身份验证时,JWT通常被用于对用户进行身份认证和授权,它被广泛地应用于多语言和多平台上简化身份验证和授权流程。

二、为什么要使用JWT

相较于传统的Session和Cookie认证方式,使用JWT认证具有以下优势:

  1. 简化开发过程,开发人员只需要实现JWT生成和校验逻辑即可,避免了很多繁琐的Session和Cookie管理任务。

  2. 分布式应用友好,在多个服务器之间共享认证状态变得更加容易。

  3. 安全性高,JWT使用基于密钥的认证方式,可以防止中间人攻击和数据篡改。

三、SpringBoot整合SpringSecurity实现JWT认证的流程

  1. 引入相关依赖

在使用SpringBoot整合SpringSecurity实现JWT认证之前,我们需要在项目中引入一些相关依赖,包括:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
  1. 实现用户认证逻辑

在进行JWT认证之前,我们需要先实现用户认证逻辑。可以通过继承SpringSecurity提供的UserDetailsService接口,实现自定义的用户认证逻辑。

@Service
public class MyUserDetailsService 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(username + " not found");
        }

        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : user.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }

        return new org.springframework.security.core.userdetails.User(user.getUsername(),
                user.getPassword(), authorities);
    }
}
  1. 实现JWT生成和校验逻辑

使用JWT进行身份认证和授权,需要实现JWT的生成和校验逻辑。这里我们可以使用jjwt库进行实现。

@Service
public class JwtTokenProvider {
    private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);

    private static String jwtSecret = "jwtSecret";

    private static int jwtExpirationInMs = 604800000;

    public static String generateToken(Authentication authentication) {
        User principal = (User) authentication.getPrincipal();

        Date expirationDate = new Date(System.currentTimeMillis() + jwtExpirationInMs);

        return Jwts.builder()
                .setSubject(principal.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }

    public static boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException ex) {
            logger.error("Invalid JWT signature");
        } catch (MalformedJwtException ex) {
            logger.error("Invalid JWT token");
        } catch (ExpiredJwtException ex) {
            logger.error("Expired JWT token");
        } catch (UnsupportedJwtException ex) {
            logger.error("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT claims string is empty.");
        }
        return false;
    }

    public static String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();

        return claims.getSubject();
    }
}
  1. 配置JWT认证过滤器

在SpringSecurity中,我们可以通过实现一个过滤器来进行JWT认证,代码如下:

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
                String username = jwtTokenProvider.getUsernameFromToken(jwt);

                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}
  1. 配置WebSecurity配置类

在配置SpringSecurity时,我们需要配置一个WebSecurity配置类,代码如下:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }

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

    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                    .and()
                .csrf()
                    .disable()
                .exceptionHandling()
                    .authenticationEntryPoint(unauthorizedHandler)
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .authorizeRequests()
                    .antMatchers("/api/auth/**")
                        .permitAll()
                    .anyRequest()
                        .authenticated();

        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

四、示例1:用户登录

在前端页面中,用户输入用户名和密码,将信息发送到后台,进行认证,并通过JWT生成和返回认证所需的Token信息,下面是示例代码:

function login(username, password) {
    return axios.post('/api/auth/login', {
        username: username,
        password: password
    }).then(response => {
        if (response.data.token) {
            localStorage.setItem('user', JSON.stringify(response.data));
        }
        return response.data;
    });
}

在后端中,我们实现了/login接口,用于接收前端发送的用户名和密码信息,并进行JWT认证和Token的生成,代码如下:

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private UserRepository userRepository;

    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = jwtTokenProvider.generateToken(authentication);
        return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
    }
}

五、示例2:使用Token进行用户认证

在前端发起请求时,在Http请求头中添加Authorization字段,值为“Bearer ”和Token内容构成的字符串,用于在后台进行认证。示例代码如下:

function getUserInfo() {
    const user = JSON.parse(localStorage.getItem('user'));

    if (user && user.token) {
        return axios.get('/api/user/me', {
            headers: { 'Authorization': 'Bearer ' + user.token }
        }).then(response => {
            return response.data;
        });
    }

    return null;
}

在后端中,我们实现了一个/me接口,用于获取当前用户的信息,请参考示例代码:

@RestController
@RequestMapping("/api/user")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @GetMapping("/me")
    @PreAuthorize("hasRole('USER')")
    public UserSummary getCurrentUser(@CurrentUser UserPrincipal currentUser) {
        UserSummary userSummary = new UserSummary(currentUser.getId(), currentUser.getUsername(), currentUser.getEmail());
        return userSummary;
    }
}

六、总结

到这里,我们已经讲解了“SpringBoot整合SpringSecurity实现JWT认证的项目实践”的完整流程,并提供了两个示例用于帮助读者理解如何在实际项目中应用JWT认证。相信读者已经掌握了相关的技能,可以在自己的项目中应用JWT认证。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot整合SpringSecurity实现JWT认证的项目实践 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • SpringBoot统一功能处理的方式详解

    Spring Boot统一功能处理的方式详解 Spring Boot是一个流行的Java框架,可以帮助开发人员快速构建和部署应用程序。在开发过程中,我们经常需要实现一些通用的功能,例如异常处理、日志记录、安全性等。本文将详细讲解如何使用Spring Boot实现统一功能处理,包括全局异常处理、日志记录、安全性等。 1. 全局异常处理 在Web应用程序中,异常…

    Java 2023年5月14日
    00
  • Java由浅入深全面讲解方法的使用

    Java由浅入深全面讲解方法的使用 什么是方法? 方法是一组可以被重复使用的代码块。它可以接受参数并返回结果。在Java中,方法是类的基本组成部分,通过方法可以完成对类的成员变量进行操作,并实现不同功能的代码块重用。 如何定义方法? 在Java中,方法由方法名和一对括号()组成,括号中可以定义传递给方法的参数列表。方法的代码块用{}包围。定义方法的基本语法如…

    Java 2023年5月26日
    00
  • spring控制事务的三种方式小结

    让我为您详细讲解一下“spring控制事务的三种方式小结”。 什么是事务 在介绍 Spring 的事务管理之前,我们需要先了解一下什么是事务。在数据库中,事务是指一组数据库操作,它们要么全部执行,要么全部不执行,是一个不可分割的工作单元。如果其中任意一条操作失败,那么整个事务就会被回滚,也就是所有已经提交的操作都会被回滚,回到事务开始前的状态。 Spring…

    Java 2023年5月20日
    00
  • java二分查找插入法

    当需要在已排序数组中查找元素时,可以使用二分查找算法。如果需要向已排序数组中插入元素,可以使用二分查找插入法。 二分查找插入法的主要思路是通过二分查找找到需要插入的元素在数组中的位置,然后将该元素插入到该位置中。以下是具体的步骤: 首先,定义需要查询的元素 target 和已排序的数组 nums,同时记录数组的左右端点 left 和 right。 计算需要查…

    Java 2023年5月19日
    00
  • 一文详解kafka序列化器和拦截器

    下面我将详细讲解“一文详解kafka序列化器和拦截器”的完整攻略。 1. 什么是Kafka序列化器? Kafka序列化器的作用是将对象序列化(编码)成字节流,以便于在Kafka集群中的各个节点之间进行传输。Kafka序列化器是Kafka生产者客户端使用的一种功能,可以将Key和Value序列化为字节数组并将其发送到Kafka broker上。Kafka提供了…

    Java 2023年5月20日
    00
  • Java 实战项目锤炼之IT设备固定资产管理系统的实现流程

    Java 实战项目锤炼之IT设备固定资产管理系统的实现流程 在这个项目中,我们将用Java语言实现一个IT设备固定资产管理系统,该系统将允许用户管理公司的固定资产,包括计算机、服务器、打印机等。具体的实现流程如下: 1. 确定需求和功能 在开发任何一个软件系统之前,我们必须针对用户的需求和要求进行调研和分析,明确系统所需要实现的功能,以便我们能够有针对性的进…

    Java 2023年5月23日
    00
  • Java数组声明、创建、初始化基础

    下面我们来详细讲解一下Java数组声明、创建、初始化基础的攻略。 什么是Java数组 数组是一种数据结构,可以用来将一系列相同类型的数据存储在同一变量名下。在Java中,数组是一个固定大小的连续内存块,每个元素可以是任何Java数据类型,包括对象和基本类型。 Java数组的声明 Java数组的声明需要明确指定数组的类型、数组名和数组大小。其基本语法如下: t…

    Java 2023年5月26日
    00
  • 四个Java必须知道的负载均衡算法分享

    下面我将详细介绍《四个Java必须知道的负载均衡算法分享》这篇攻略。 标题 介绍 这篇攻略主要介绍了Java程序员在实际开发中需要掌握的四种负载均衡算法,并从算法原理、应用场景、优缺点等多方面进行了详细的分析。 四种负载均衡算法 轮询算法 轮询算法是指按照请求的顺序,依次将请求分配到每个服务器,由于是平均分配请求,所以该算法的负载均衡效果较为稳定。 随机算法…

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