SpringBoot集成SpringSecurity和JWT做登陆鉴权的实现

下面是详细的讲解和示例:

一、SpringBoot集成SpringSecurity和JWT的基础配置

Spring Security 是一款强大、灵活并且广泛使用的安全框架,它基于 Spring 构建,提供了一种基于角色的访问控制、认证和授权等安全解决方案。而 JWT 是一种轻量级的认证机制,它可以在用户和服务器之间进行授权传递,用于跨域认证。在本文中,我们将讨论如何将 Spring Boot、Spring Security 和 JWT 整合在一起,从而实现基于 JWT 的认证和授权机制。

首先,我们需要添加相关依赖,具体内容如下:

<!-- Spring Boot -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- JWT -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.1</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.1</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.1</version>
    <scope>runtime</scope>
</dependency>

其中, spring-boot-starter-web 是 Spring Boot 的 Web 依赖, spring-boot-starter-security 是 Spring Security 的依赖, jjwt-api 是 JWT API 的依赖, jjwt-impl 是 JWT 实现的依赖, jjwt-jackson 是用于 JWT 数据反序列化的依赖。

接下来,我们需要进行一些基础的配置,具体内容如下:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Bean
    public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
        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.csrf().disable()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated();

        // 添加JWT filter
        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }
}

在上述配置中,我们首先注入了两个对象,分别是 UserDetailsServiceJwtAuthenticationEntryPointUserDetailsService 是一个用于获取用户相关信息的接口,我们需要自定义它来获取用户信息。JwtAuthenticationEntryPoint 则是在用户认证失败时被调用,返回未认证的错误信息。

接着,我们创建了一个 JwtAuthenticationFilter 的实例,并将它注入到 Bean 中。JwtAuthenticationFilter 是我们自定义的 JWT 过滤器,该过滤器用于解析 JWT 并验证用户信息。当请求带有 JWT 的 Authorization 头信息时将自动进入该过滤器的处理流程。

接下来,我们通过重写 configure(AuthenticationManagerBuilder auth)configure(HttpSecurity http) 方法来对 Spring Security 进行配置。其中, configure(AuthenticationManagerBuilder auth) 方法用于配置用户信息服务和密码加密方式, configure(HttpSecurity http) 方法用于配置 Web 安全性。

configure(HttpSecurity http) 方法中,我们首先禁用了 Spring Security 对 CSRF(跨站请求伪造)的防护。接着,我们将会话管理策略设置为无状态,这意味着用户不需要记录会话的状态信息。在本文的示例中,我们将使用 JWT Token 来代替会话。

最后,我们通过 http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class) 来为所有请求添加了一个自定义的 JWT 过滤器,用于解析 JWT 并验证用户信息。

上述配置是 Spring Security 整合 JWT 的基础配置,接下来我们需要编写具体的实现类。

二、SpringBoot集成SpringSecurity和JWT实现用户认证和授权

1. 用户认证接口实现

我们需要实现一个用户认证接口,用于处理用户的登录请求。具体代码如下:

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginForm loginForm) {

        // 用户认证
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginForm.getUsername(),
                        loginForm.getPassword()
                )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        // 生成JWT Token
        UserDetails userDetails = userDetailsService.loadUserByUsername(loginForm.getUsername());
        String token = jwtTokenUtil.generateToken(userDetails);

        return ResponseEntity.ok(new JwtResponse(token));
    }
}

在上述代码中,我们首先注入了三个对象,分别是 AuthenticationManagerJwtTokenUtilUserDetailsService。然后,我们编写了一个 login() 方法来处理用户认证请求。

该方法首先使用 AuthenticationManager 对象进行用户认证,如果用户名和密码均正确,则返回一个 Authentication 对象。接着,我们将认证状态存储在 SecurityContextHolder 中。

最后,我们通过 userDetailsService.loadUserByUsername(loginForm.getUsername()) 方法获取用户信息,并使用 jwtTokenUtil.generateToken(userDetails) 方法生成 JWT Token。最后通过 ResponseEntity 将 Token 返回给客户端。

2. JWT 过滤器实现

我们也需要实现一个 JWT 过滤器,用于拦截所有请求并进行 JWT 解析和用户认证。具体代码如下:

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        final String header = request.getHeader(HttpHeaders.AUTHORIZATION);

        if (header == null || !header.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        final String token = header.substring(7);
        String username = jwtTokenUtil.getUsernameFromToken(token);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            if (jwtTokenUtil.validateToken(token, userDetails)) {

                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

        filterChain.doFilter(request, response);
    }
}

在上述代码中,我们首先注入了两个对象,分别是 JwtTokenUtilUserDetailsService。具体实现可以看到,我们从 HTTP 请求头信息中获取 JWT Token,然后使用该 Token 获取到用户信息并进行用户认证。

如果 Token 无效,则认证失败;否则,我们创建一个 Authentication 对象并将其设置到 SecurityContextHolder 中。

三、示例

下面是一些用于测试的示例代码。

1. 用户类

public class User implements UserDetails {

    private Integer id;
    private String username;
    private String password;
    private boolean isEnabled;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean isEnabled() {
        return isEnabled;
    }

    public void setEnabled(boolean enabled) {
        isEnabled = enabled;
    }

    // other UserDetails methods
}

2. 用户服务类实现

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = new User();
        user.setId(1);
        user.setUsername(username);
        user.setPassword(new BCryptPasswordEncoder().encode("password"));
        user.setEnabled(true);

        return user;
    }
}

3. JWT Token 工具类实现

@Component
public class JwtTokenUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "iat";
    private static final String SECRET = "jwtsecret";

    private static final long EXPIRATION_TIME = 864_000_000; // 10天,以毫秒为单位

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();

        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());

        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        String username;

        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }

        return username;
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);

        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims;

        try {
            claims = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }

        return claims;
    }

    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + EXPIRATION_TIME);
    }

    private boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }
}

4. 登录表单实现

public class LoginForm {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

以上就是 Spring Boot 集成 Spring Security 和 JWT 的实现过程和示例代码。希望此篇文章对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot集成SpringSecurity和JWT做登陆鉴权的实现 - Python技术站

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

相关文章

  • java控制台输出版多人聊天室

    Java控制台输出版多人聊天室是一种基于Java的多用户聊天程序,可以让多个用户在同一时间内进行聊天并且可以同时发送和接收消息。以下是一些步骤和示例,可以让您快速了解该聊天室的使用方法。 步骤: Step 1:编写代码 首先,需要编写Java代码来创建多人聊天室。这个过程可能相对复杂,涉及到网络编程以及多线程处理等知识点。因此,您可以参考其他开源项目或教程来…

    Java 2023年5月26日
    00
  • MyBatisPlus超详细分析条件查询

    以下是针对“MyBatisPlus超详细分析条件查询”的完整攻略: 一、MyBatisPlus概述 MyBatisPlus是对MyBatis进行了功能扩展和优化的一款工具。其提供了更加便捷的CRUD操作、Lambda表达式查询等功能,大大提升了开发效率。 二、条件查询 MyBatisPlus提供了多种条件查询的方式,包括wrapper、Lambda以及Que…

    Java 2023年5月20日
    00
  • 2020最新版MyBatis高频面试题

    2020最新版MyBatis高频面试题攻略 什么是 MyBatis? MyBatis 是一款基于 Java 语言持久层框架,类似于 Hibernate。它可以将 SQL 语句与 Java 对象映射,方便地进行数据库的访问。 MyBatis 的核心组件是什么? MyBatis 的核心组件分别为: SqlSessionFactoryBuilder SqlSess…

    Java 2023年5月19日
    00
  • 详解Spring Kafka中关于Kafka的配置参数

    下面我来详细讲解一下关于“详解Spring Kafka中关于Kafka的配置参数”的完整攻略。 1. Kafka中常用的配置参数 在使用Kafka时,可以通过配置不同的参数来更加灵活地自定义Kafka的行为。下面是Kafka中一些常用的配置参数: bootstrap.servers:Kafka集群的连接地址列表,指定了Kafka Broker的主机名和端口号…

    Java 2023年5月20日
    00
  • java切分字符串的2种方法实例

    按照要求,我将为你提供一篇“java切分字符串的2种方法实例”的完整攻略,涵盖以下内容: 什么是字符串切分? 方法一:使用String类的split()方法 说明如何使用split()方法实现字符串切分 给出一个使用split()方法切分字符串的示例 方法二:使用java.util.regex.Pattern类的split()方法 说明如何使用Pattern…

    Java 2023年5月26日
    00
  • 教你如何使用Java多线程编程LockSupport工具类

    教你如何使用Java多线程编程LockSupport工具类 什么是LockSupport LockSupport是JavaSE 5引入的一个工具类,用于线程的阻塞和唤醒。它可以在任何时刻,让一个正在运行的线程阻塞或者唤醒它。 LockSupport的使用 park()方法 LockSupport类下的park方法可以阻塞线程,直到调用它的unpark方法或者…

    Java 2023年5月18日
    00
  • hibernate测试时遇到的几个异常及解决方法汇总

    Hibernate测试时遇到的几个异常及解决方法汇总 在使用Hibernate进行开发时,我们常常会遇到各种异常以及错误提示,本文将总结一些常见的异常及其解决方法。 环境搭建异常:ClassNotFountException 在进行Hibernate的开发之前,我们需要搭建好相应的开发环境,如JDK、IDE、数据库等。如果其中的某一个组件环境没有搭建好,可能…

    Java 2023年5月19日
    00
  • Java 生成随机字符串数组的实例详解

    Java 生成随机字符串数组的实例详解 介绍 在Java中,我们经常需要使用随机字符串数组来做一些初始化操作,这时就需要用到生成随机字符串数组的方法了。本文将介绍Java生成随机字符串数组的详细攻略。 实现步骤 生成随机字符串数组的步骤如下: 定义生成的字符串的长度 定义生成的字符串数组的长度 生成随机字符串 将随机字符串添加到字符串数组中 返回字符串数组 …

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