详解SpringBoot+SpringSecurity+jwt整合及初体验

详解SpringBoot+SpringSecurity+jwt整合及初体验

本文将详细讲解如何将SpringBoot、SpringSecurity和jwt整合起来实现用户认证与授权功能,包含完整的代码和详细的步骤,最终实现一个简单的用户登录验证功能。

环境准备

  • JDK 1.8
  • Maven 3.x
  • IDE: 推荐使用IntelliJ IDEA
  • Postman:用于测试接口

创建SpringBoot项目

在IDE中创建一个SpringBoot项目,选择Maven作为构建工具,在pom.xml文件中添加如下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId> // web组件,提供web服务
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId> // security组件,提供安全控制
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId> // jwt组件,提供json web token相关功能
        <version>0.9.1</version>
    </dependency>
</dependencies>

配置SpringSecurity

在SpringBoot项目的application.properties文件中添加如下配置:

spring.security.user.name=admin
spring.security.user.password=admin
spring.security.user.roles=ADMIN

该配置表示创建一个用户名为admin,密码为admin,角色为ADMIN的用户。在实际应用中应该使用加密后的密码。

配置JWT

application.properties文件中添加如下配置:

jwt.secret=secret-key
jwt.expiration-in-seconds=604800

该配置表示使用secret-key作为jwt的密钥,jwt的过期时间为一周。

创建一个JwtUtils类用于生成和解析jwt:

@Component
public class JwtUtils {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration-in-seconds}")
    private int expirationInSeconds;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expirationInSeconds * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    private boolean isTokenExpired(String token) {
        Date expirationDate = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getExpiration();
        return expirationDate.before(new Date());
    }
}

编写登录接口

创建一个AuthController类,实现用户登录接口:

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

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtils jwtUtils;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody AuthenticationRequest request) {
        try {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
            SecurityContextHolder.getContext().setAuthentication(authentication);
            String token = jwtUtils.generateToken((UserDetails) authentication.getPrincipal());
            return ResponseEntity.ok(new AuthenticationResponse(token));
        } catch (AuthenticationException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
    }
}

其中AuthenticationRequest表示前端传递的登录请求:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthenticationRequest implements Serializable {
    private String username;
    private String password;
}

AuthenticationResponse表示登录成功后返回的jwt:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthenticationResponse implements Serializable {
    private String jwtToken;
}

配置Spring Security

创建一个JwtAuthenticationFilter类用于从Authorization头部解析jwt,并将用户信息存入Spring Security上下文:

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String authorizationHeader = request.getHeader("Authorization");

        if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith("Bearer ")) {
            String jwt = authorizationHeader.substring(7);
            String username = jwtUtils.getUsernameFromToken(jwt);

            if (StringUtils.hasText(username) && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);

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

        filterChain.doFilter(request, response);
    }
}

WebSecurityConfigurerAdapter中配置JwtAuthenticationFilter,并启用csrf保护:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/auth/**")
                .permitAll()
                .anyRequest()
                .authenticated();
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

测试登录接口

在Postman中发送POST请求:http://localhost:8080/api/auth/login,Headers中加入Content-Type: application/json,请求体中加入:

{
    "username": "admin",
    "password": "admin"
}

运行后会得到如下结果:

{
    "jwtToken": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTYzMzUzMjExOSwiZXhwIjoxNjMzNTU4MTE5fQ.2HVXYQNB5zWgR83SjlYvKvAD-VqyZTbEmyIy_t-sZJ9OdMCuLq0nLWOD4cT8QjL9wUyS9dUly4v6dFYJ7cmRXw"
}

将得到的jwt token放入请求头中继续发送请求,后端会自动将用户信息存入Spring Security上下文中,实现了用户认证与授权功能。

示例1:自定义UserDetailsService

如果需要使用自己实现的UserDetailsService来加载用户信息,可以按照下面的步骤来实现:

  1. 创建一个User类,表示应用中的用户:

```java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {

   private String username;
   private String password;
   private List<GrantedAuthority> authorities;

   @Override
   public Collection<? extends GrantedAuthority> getAuthorities() {
       return authorities;
   }

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

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

   @Override
   public boolean isAccountNonExpired() {
       return true;
   }

   @Override
   public boolean isAccountNonLocked() {
       return true;
   }

   @Override
   public boolean isCredentialsNonExpired() {
       return true;
   }

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

}
```

  1. 创建一个实现了UserDetailsService接口的类,用于从数据源中加载用户信息:

```java
@Service
public class MyUserDetailsService implements UserDetailsService {

   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       // 查询数据库并返回UserDetails
       return new User(username, "password", List.of(new SimpleGrantedAuthority("ROLE_USER")));
   }

}
```

  1. WebSecurityConfigurerAdapter中注入UserDetailsService实现类,进行配置:

```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Autowired
   private MyUserDetailsService userDetailsService;

   // 省略其他配置

}
```

示例2:自定义加密

默认情况下,Spring Security使用BCrypt进行加密,如果需要使用自己的加密方式,可以按照下面的步骤来实现:

  1. 创建一个实现了PasswordEncoder接口的类,用于加密和校验密码:

```java
@Component
public class MyPasswordEncoder implements PasswordEncoder {

   @Override
   public String encode(CharSequence rawPassword) {
       // 自定义加密方法
       return rawPassword.toString() + "hashed";
   }

   @Override
   public boolean matches(CharSequence rawPassword, String encodedPassword) {
       // 自定义校验方法
       return encodedPassword.equals(rawPassword.toString() + "hashed");
   }

}
```

  1. MyPasswordEncoder类注入到Spring容器中:

java
@Bean
public MyPasswordEncoder myPasswordEncoder() {
return new MyPasswordEncoder();
}

  1. WebSecurityConfigurerAdapter中配置使用自定义的PasswordEncoder

```java
@Autowired
private MyPasswordEncoder passwordEncoder;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
```

这样就可以使用自己的加密方式来保护密码了。

总结

本文介绍了如何将SpringBoot、SpringSecurity和jwt整合起来实现用户验证与授权功能,包含完整的代码和详细的步骤,最终实现了一个简单的用户登录验证功能,同时提供了两个实际应用的示例。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解SpringBoot+SpringSecurity+jwt整合及初体验 - Python技术站

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

相关文章

  • JavaWeb实现图形报表折线图的方法

    下面就是JavaWeb实现图形报表折线图的方法的完整攻略: 1. 准备工作 在实现JavaWeb图形报表折线图前,我们需要先准备好以下资源: 前端使用的图表库,例如ECharts、Highcharts等; 后端使用的JavaWeb框架,例如Spring、Struts2等; 数据库,用于存储数据; 数据库连接池,用于连接数据库。 2. 使用ECharts绘制折…

    Java 2023年6月15日
    00
  • java Struts2 在拦截器里的跳转问题

    针对“java Struts2 在拦截器里的跳转问题”的完整攻略,我来逐步讲解及演示示例。 1. Struts2 拦截器介绍 Struts2 是一个由 Apache 组织推出的开源的 JavaEE Web 应用框架。在构建应用时,Struts2 利用了一种称为拦截器(Interceptor) 的机制,以实现动态地改变应用程序处理请求的流程。简单来说,拦截器是…

    Java 2023年5月19日
    00
  • Spring MVC 自定义数据转换器的思路案例详解

    Spring MVC 自定义数据转换器的思路案例详解 Spring MVC 是一个非常流行的 Java Web 框架,它提供了很多便捷的功能,其中包括数据转换器。数据转换器可以将请求参数转换为 Java 对象,或将 Java 对象转换为响应参数。Spring MVC 默认提供了很多数据转换器,但有时候我们需要自定义数据转换器来满足特定的需求。本文将详细讲解 …

    Java 2023年5月18日
    00
  • Java日期工具类的封装详解

    下面我将详细介绍“Java日期工具类的封装详解”的攻略。 什么是Java日期工具类? Java日期工具类是Java中用于处理日期和时间的类库。使用日期工具类可以方便地进行日期和时间的转换、计算、比较等操作,提高程序的可靠性和效率。 常用的Java日期工具类有哪些? Java中内置了很多日期工具类,常用的有: java.util.Date类:表示日期和时间的类…

    Java 2023年5月20日
    00
  • Spring依赖注入的三种方式实例详解

    让我们来详细讲解一下“Spring依赖注入的三种方式实例详解”。 1. 依赖注入 在 Spring 框架中,依赖注入是一种对象创建方式,通常是在构造函数、setter 方法或工厂方法中注入依赖对象。 依赖注入通过在运行时动态注入所需的依赖对象,从而增加了代码的可读性和可维护性,并且减少了类之间的关联性,使代码更加灵活和可扩展。 2. 三种依赖注入方式 在 S…

    Java 2023年6月16日
    00
  • struts2中simple主题下标签默认样式的移除方法

    在Struts2中,使用simple主题时,当表单校验出错后,错误信息会显示在标签中。如果默认的样式不符合我们的需求,我们需要对其进行自定义。以下是移除标签默认样式的完整攻略。 1. 引入CSS文件 在JSP中添加如下代码引入CSS文件: <head> <link rel="stylesheet" type="…

    Java 2023年5月20日
    00
  • Java 异常的栈轨迹(Stack Trace)详解及实例代码

    Java 异常的栈轨迹,简称 Stack Trace,是指当 Java 程序在发生异常时,系统会自动生成一个异常堆栈信息,记录异常发生的位置、异常类型、异常信息等具体信息。通过 Stack Trace 信息,我们可以定位问题所在,进而快速排除代码中的异常。 以下是详细的攻略步骤: 1. 什么是 Stack Trace Stack Trace 是一种异常堆栈信…

    Java 2023年5月27日
    00
  • java 验证用户是否已经登录与实现自动登录方法详解

    下面是关于“java 验证用户是否已经登录与实现自动登录方法详解”的完整攻略: 1. 验证用户是否已经登录 在web应用程序中,用户登录状态验证通常在服务器端进行。验证用户是否已经登录通常是通过以下几个步骤实现: 在登录页面中,用户输入用户名和密码,并提交表单。 将提交的表单数据传到服务器端,并在服务器端与用户信息进行比对。 如果用户信息正确,则将用户的登录…

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