解析SpringSecurity+JWT认证流程实现

yizhihongxing

下面我将为大家详细讲解 "解析SpringSecurity+JWT认证流程实现" 的完整攻略。

1. JWT简介

JSON Web Token(JWT)是一种定义了一种紧凑且自包含的方式,可以用于将各种信息传递给另一个系统。JWT 在 Web 应用中得到广泛的应用,其最大的优势就是可以在客户端和服务器之间,通过方式方便快捷的的方式实现身份认证和授权。

JWT由三部分组成:头部(header)、载荷(payload)、签名(signature)。

  • 头部(header):由两部分组成,一是声明 JWT 的类型,二是声明所使用的算法。
  • 载荷(payload):实际存放有用信息的地方,JWT 标准定义了7个官方字段,同时你也可以定义自己的字段,常用的有4个:

  • iss(issuer):签发JWT的人

  • sub(subject):设置JWT面向的对象,一般是用户
  • exp(expiration):过期时间
  • iat(issued at):签发时间

  • 签名(signature):由头部、载荷及秘钥生成的签名,用于认证数据的确来自于发送方且未被篡改。

2. Spring Security 简介

Spring Security 是 Spring 框架中的一个安全框架,集成了许多企业级的安全控制功能。Spring Security 基本实现了 Web 应用安全控制的三大主要功能:认证、授权和攻击防护。

3. Spring Security + JWT 实现认证流程

Spring Security 和 JWT 相结合的认证流程大概分为以下步骤:

  1. 用户向服务器发送用户名和密码(或其他身份验证信息)
  2. 服务器收到请求后,验证用户名和密码
  3. 验证通过后,服务器生成 JWT
  4. 服务器将 JWT 返回给用户
  5. 用户每次向服务器发送请求时,需要在 HTTP 头中带上 JWT
  6. 服务器通过解码 JWT 来验证用户的身份

下面,我们结合 Spring Security + JWT 实现认证流程的完整攻略。

3.1 添加依赖库

在 pom.xml 文件中添加如下依赖库:

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

<!-- JWT 依赖库 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jjwt.version}</version>
</dependency>

3.2 配置 Spring Security

在 Spring Security 中,我们需要配置身份验证 (Authentication) 和授权 (Authorization) 的逻辑。

3.2.1 配置身份验证逻辑

通常,Spring Security 的身份验证逻辑都会写在自定义的 SecurityConfig 类中。下面我们先定义 JwtUser 和 JwtUserDetails 两个类,表示登录用户和用户细节信息:

@Data
public class JwtUser implements UserDetails {
    private Long id;
    private String username;
    private String password;
    private List<GrantedAuthority> authorities;

    // ... getter, setter, overrides
}

public class JwtUserDetails {
    private Long id;
    private String username;
    private String password;
    private List<String> roles;

    public JwtUserDetails(Long id, String username, String password, List<String> roles) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.roles = roles;
    }

    public List<GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : roles) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }
}

其中,JwtUser 是 UserDetails 接口的实现类,JwtUserDetails 则是 JwtUser 的详细信息,它里面包含了用户的角色(Role)等信息。

接下来,我们需要自定义一个 AuthenticationProvider 实现类,用于验证用户身份和密码:

@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtTokenUtils jwtTokenUtils;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();

        JwtUserDetails userDetails = (JwtUserDetails) jwtUserDetailsService.loadUserByUsername(username);

        if (!password.equals(userDetails.getPassword())) {
            // 密码错误
            throw new BadCredentialsException("Invalid username or password");
        }

        JwtUser jwtUser = new JwtUser(userDetails.getId(), userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());

        // 生成 JWT
        String jwtToken = jwtTokenUtils.generateToken(jwtUser);

        return new UsernamePasswordAuthenticationToken(username, jwtToken, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

在身份验证逻辑中,我们需要注入 JwtUserDetailsService 和 JwtTokenUtils,其中 JwtTokenUtils 用于生成 JWT。

3.2.2 配置授权逻辑

在 Spring Security 中,我们可以通过配置 HttpSecurity 实现请求授权。以下是示例代码:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;

    // 自定义身份验证逻辑
    @Autowired
    private JwtAuthenticationProvider jwtAuthenticationProvider;

    // 忽略验证的路径
    private static final String[] AUTH_WHITELIST = {
            "/login",
            "/logout"
    };

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()
                .cors()
                .and()
                .authorizeRequests()
                    .antMatchers(AUTH_WHITELIST).permitAll()
                    .anyRequest().authenticated()
                .and()
                // 使用自定义身份验证逻辑
                .authenticationProvider(jwtAuthenticationProvider)
                // 添加自定义验证过滤器,用于从请求头中获取JWT并进行身份验证
                .addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
                .headers()
                .cacheControl();
    }
}

在授权逻辑中,我们指定了白名单路径和自定义身份验证逻辑。其中,JwtAuthorizationFilter 是一个自定义的 Filter,它用于从请求头中获取 JWT 并进行身份验证。

3.3 配置 JWT

在 Spring Security 中,我们通过自定义拦截器 Filter 来获取并解析 JWT。下面是示例代码:

@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenUtils jwtTokenUtils;

    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String jwtToken = request.getHeader("Authorization");

        if (!StringUtils.isEmpty(jwtToken)) {
            String username = jwtTokenUtils.getUsernameFromToken(jwtToken);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);
                if (jwtTokenUtils.validateToken(jwtToken, userDetails)) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }

        filterChain.doFilter(request, response);
    }
}

在 JwtAuthorizationFilter 中,我们通过配置和注入 JwtTokenUtils 和 JwtUserDetailsService,并从请求头中获取 JWT,然后解析 JWT 并验证用户身份。

3.4 示例

下面我们模拟一个用户登录和获取数据的过程。

我们可以在 UserController 中添加以下两个方法:

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody Map<String, String> user) {
        String username = user.get("username");
        String password = user.get("password");

        // 构造一个 Authentication 对象,用于 Spring Security 做身份验证
        Authentication authentication = new UsernamePasswordAuthenticationToken(username, password);

        // 调用 AuthenticationManager 的 authenticate 方法进行身份验证
        Authentication result = authenticationManager.authenticate(authentication);

        // 登录成功后,将 JWT 和用户信息一同返回给前端
        JwtUser jwtUser = (JwtUser) result.getPrincipal();
        Map<String, String> response = new HashMap<>();
        response.put("username", jwtUser.getUsername());
        response.put("token", (String) result.getCredentials());
        return ResponseEntity.ok(response);
    }

    @GetMapping("/data")
    public List<String> getData() {
        return Arrays.asList("data1", "data2", "data3", "data4");
    }
}

在为 /user/login 这个接口配置 JwtAuthenticationProvider 之后,可以直接调用这个接口完成身份认证,并获取 JWT。

curl -X POST -H "Content-Type: application/json" \
    -d '{"username": "alice", "password": "123456"}' \
    http://localhost:8080/user/login

假设身份认证成功后,返回的 JSON 中包含了 JWT,接着我们可以使用这个 JWT 向 /user/data 接口请求数据:

curl -H "Authorization: [JWT]" \
    http://localhost:8080/user/data

3.5 注意事项

以上是结合 Spring Security 和 JWT 实现了登录认证的流程,但需要注意以下问题:

  • JWT 需要使用 HTTPS 安全通道传输,防止 JWT 被窃取。
  • JWT 的数据量不宜过大,否则将影响传输效率,可以使用对称加密或非对称加密来减少签名长度,但需要权衡安全和效率。
  • JWT 需要设置过期时间,通常是在生成 JWT 的时候设置一个时间戳,超过时间戳则 JWT 失效。
  • JWT 需要妥善保管,防止 JWT 被窃取。
  • 在验证 JWT 的签名时,需要使用同一的密钥(key),否则会验证不过。

4. 总结

以上就是解析 Spring Security + JWT 认证流程实现的完整攻略,我们可以通过简单的配置和代码实现一个标准的身份认证和授权逻辑,同时使用 JWT 来保护用户身份和数据。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解析SpringSecurity+JWT认证流程实现 - Python技术站

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

相关文章

  • 在Action中以Struts2的方式输出JSON数据的实例

    下面是“在Action中以Struts2的方式输出JSON数据的实例”完整攻略,过程中包含两条示例: 1. 添加Struts2-json-plugin插件 在使用Struts2输出JSON格式数据之前,我们需要先添加Struts2-json-plugin插件,该插件可以将Java对象转换为JSON格式数据并输出到浏览器。在pom.xml文件中添加以下代码即可…

    Java 2023年5月20日
    00
  • Java实现上传Excel文件并导入数据库

    首先,要实现上传Excel文件并导入数据库,需要使用Java语言中的POI库来进行Excel文件的解析,同时需要使用数据库连接池实现连接管理。下面是详细的实现步骤。 1. 上传Excel文件 首先,需要在页面上实现上传文件的功能,可以使用form表单和input标签的type为file的属性来实现: <form action="upload&…

    Java 2023年5月20日
    00
  • Java数据库连接_jdbc-odbc桥连接方式(详解)

    Java数据库连接_jdbc-odbc桥连接方式(详解) 简介 JDBC(Java Database Connectivity)是Java标准化技术之一,其功能是提供Java程序与各种关系型数据库建立连接及操作数据库的通用接口。而JDBC-ODBC桥连接方式是Java连接ODBC(Open Database Connectivity)的一种方式。 ODBC简…

    Java 2023年5月19日
    00
  • 纯Java代码实现流星划过天空

    下面是纯Java代码实现流星划过天空的完整攻略。 步骤一:实现画布 首先需要使用Java的GUI库,比如Swing或JavaFX,来创建一个窗口,并在窗口上绘制流星。 使用JavaFX实现画布 import javafx.application.Application; import javafx.scene.Group; import javafx.sce…

    Java 2023年5月26日
    00
  • freemarker 数字格式化深入理解

    Freemarker是一个功能强大的Java模板引擎,它可以帮助我们以更加灵活和高效的方式生成各种文本格式。数字格式化是Freemarker的一个重要特性,它提供了许多选项来格式化数字类型数据,并且还支持各种自定义格式化方式。本文将深入探讨Freemarker数字格式化的原理和用法。 数字格式化语法 Freemarker的数字格式化语法类似于Java的Dec…

    Java 2023年6月15日
    00
  • SQL Server 2008 连接JDBC详细图文教程

    SQL Server 2008 连接JDBC详细图文教程 1. 下载驱动程序 在连接 SQL Server 2008 数据库之前,需要先下载并安装相应的 JDBC 驱动程序。可通过以下步骤下载: 进入 Microsoft 官网下载页面; 选择适用于 Java 的 Microsoft JDBC 驱动器版本; 点击“下载”按钮开始下载。 2. 安装驱动程序 下载…

    Java 2023年6月16日
    00
  • 浅谈springmvc 通过异常增强返回给客户端统一格式

    以下是关于“浅谈SpringMVC通过异常增强返回给客户端统一格式”的完整攻略,其中包含两个示例。 浅谈SpringMVC通过异常增强返回给客户端统一格式 在SpringMVC中,我们可以通过异常增强的方式来统一处理异常,并将异常信息以统一的格式返回给客户端。在本文中,我们将讲解如何通过异常增强的方式来实现这一功能。 异常增强实现原理 SpringMVC通过…

    Java 2023年5月17日
    00
  • JAVA各种OOM代码示例与解决方法

    “JAVA各种OOM代码示例与解决方法”攻略 什么是OOM OOM是”Out Of Memory”的缩写,意为内存用尽。Java程序中,如果申请内存超过了JVM所限制的最大内存,则会发生OOM异常。在Java程序中,由于内存泄漏或者分配内存过多导致的OOM异常较为常见。 OOM异常的种类 Java heap spaceJVM使用的java堆内存中没有多余的空…

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