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日

相关文章

  • 详解Spring Security如何在权限中使用通配符

    首先,在使用Spring Security进行权限管理时,有时希望通过通配符来进行权限的配置。通配符可以使得权限的配置更为灵活,方便进行管理。 在Spring Security中,可以使用Ant风格的通配符来进行权限的配置。Ant风格的通配符包含两种符号:*和**。其中,*表示任意单词,**表示任意多级目录。 例如,假设我们有以下两个URL需要进行权限配置:…

    Java 2023年6月3日
    00
  • springboot整合redis修改分区的操作流程

    下面是关于“springboot整合redis修改分区的操作流程”的完整攻略: 操作流程 修改redis.conf文件 在redis.conf配置文件中搜索”hash-max-ziplist-entries”和“hash-max-ziplist-value”两个参数。这两个参数决定了Redis使用ziplist存储hash类型的数据结构时,ziplist中的…

    Java 2023年5月20日
    00
  • Javaweb实战之实现蛋糕订购系统

    Javaweb实战之实现蛋糕订购系统攻略 1. 第一步:环境搭建 在开始实现蛋糕订购系统前,需要搭建好开发环境。首先需要安装JDK和Tomcat,并且配置好环境变量。 其中JDK是Java开发包,Tomcat是一个开放源代码的Web应用服务器,主要用于处理Java Servlet和JavaServer Pages。 2. 第二步:数据库设计 在开始编写代码前…

    Java 2023年5月20日
    00
  • java实现输出文件夹下某个格式的所有文件实例代码

    下面是详细的攻略: 1. 获取文件夹下所有文件 为了获取一个文件夹下的所有文件,我们可以使用Java中的File类和递归算法。可以先定义一个方法,传入文件夹的路径,使用该方法时传入希望查找的文件格式。 import java.io.File; public class FileUtil { /** * 获取指定文件夹下某一类型的所有文件 * * @param…

    Java 2023年5月20日
    00
  • 深入了解Java Object类的使用

    深入了解Java Object类的使用 在Java中,所有类都继承自Object类,并且Object类提供了许多有用的方法,因此了解Object类的使用可以帮助我们更好地编写Java代码。本文将深入介绍Java Object类的使用方法,包括toString()、equals()、hashCode()和getClass()方法等。 toString()方法 …

    Java 2023年5月26日
    00
  • Java8之Stream流代替For循环操作

    让我来为你详细讲解“Java8之Stream流代替For循环操作”的完整攻略。 概述 Stream是Java8中引入的一种处理数据集合的新特性,可以被用来代替传统的for循环操作,能够提高代码的可读性和运行效率。 Stream入门 首先需要对基本的Stream概念做一些了解。 Stream是数据的一种抽象,它不是一种数据结构,不保存数据,而是提供一个数据集合…

    Java 2023年5月26日
    00
  • java 设计模式(DAO)的实例详解

    针对“Java设计模式(DAO)的实例详解”,我可以提供以下攻略: Java设计模式(DAO)的实例详解 什么是DAO模式? DAO是Data Access Object的缩写,它是一种用于访问数据库的设计模式。DAO模式通过把对数据库操作的行为封装到一个单独的类或接口中,使得我们能够把业务逻辑与数据访问逻辑分离,提高了代码的可维护性和可扩展性。 DAO模式…

    Java 2023年5月19日
    00
  • log4j使用教程详解(怎么使用log4j2)

    log4j使用教程详解(怎么使用log4j2) 介绍 log4j是一个Java语言的日志记录工具,可以对程序进行日志记录,允许程序员控制日志记录输出的目的地、日志记录内容等。log4j2是log4j的升级版。本文将详细讲解如何使用log4j2。 步骤 1. 引入log4j2到项目中 在工程目录下找到 pom.xml 或 gradle.build 文件,在文件…

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