0
点赞
收藏
分享

微信扫一扫

使用JWT的Spring Security小例子

小北的爹 2022-01-20 阅读 83

JWT介绍和Spring Security的小例子都有了,接下来,我们来编写一个使用JWT作为验证方式的Spring Security的小程序。

本文是上一篇Spring Security小例子的扩展,区别在于:

  1. 使用JWT token
  2. 使用MySQL数据库存储信息

代码

本文的具体源代码请参考 https://github.com/dukeding/spring-boot-security-jwt_0116

注意事项

  1. 该程序使用了MySQL数据库,所以请先在运行环境中安装并配置好MySQL。注意,在 application.properties 文件里配置了数据源,请确保端口,DB,用户名,密码都设置正确。
  2. 不需要在DB中手工创建table,运行程序时会自动创建 USERSROLES 等table。但要注意,需要手工在 ROLES table中插入几条记录:
INSERT INTO roles(name) VALUES('ROLE_USER');
INSERT INTO roles(name) VALUES('ROLE_MODERATOR');
INSERT INTO roles(name) VALUES('ROLE_ADMIN');

程序功能

  • 用户可以注册新账号,或者使用username和password登录
  • 用户通过其角色(admin,moderator,user)来被授权访问资源

具体API如下:

MethodURI说明
POST/api/auth/signup注册新账号
POST/api/auth/signin登录
GET/api/test/all获取公开信息(无需登录)
GET/api/test/user获取User信息
GET/api/test/mod获取Moderator信息
GET/api/test/admin获取Admin信息

程序架构

在这里插入图片描述从图中可见,从左到右,该程序可分为3层:

  • HTTP
  • Spring Security
  • REST API

其中重点是 Spring Security 层,也就是蓝框所框起来的部分。它位于HTTP和REST API之间,所负责的事情包括:

  • 接收HTTP请求
  • 过滤
  • 验证(authenticate)
  • 存储Authentication数据
  • 生成token
  • 获取User详细信息
  • 授权(authorize)
  • 处理异常
  • ……

其中一些术语简介如下:

  • SecurityContextHolder 提供对SecurityContext的访问
  • SecurityContext 持有Authentication以及可能的与request有关的安全信息
  • Authentication 代表那些包含“反映了授予principal的application级别的permission的GrantedAuthority”的principal
  • UserDetails 包含了从DAO或者其它安全数据构建Authentication对象所需要的信息
  • UserDetailsService 协助从基于字符串的username创建UserDetails,通常被AuthenticationProvider使用。UserDetailsService通过Spring Data JPA与MySQL数据库连接
  • JwtAuthTokenFilter (扩展了OncePerRequestFilter)预处理HTTP请求,从token,创建Authentication并将其注入到SecurityContext
  • JwtProvider 校验,解析token字符串,或者从UserDetails生成token字符串
  • UsernamePasswordAuthenticationToken 从登录请求获取username/password,并结合到Authentication 接口的实例中
  • AuthenticationManager 使用DaoAuthenticationProvider(由UserDetailsService和PasswordEncoder协助)来校验UsernamePasswordAuthenticationToken的实例,然后对于成功的身份验证,返回一个填充好的Authentication实例
  • SecurityContext 调用SecurityContextHolder.getContext().setAuthentication(…​)(以及上面所返回的Authentication对象)所搭建
  • AuthenticationEntryPoint 处理AuthenticationException
  • 对Restful API的访问受保护于HTTPSecurity,并由Method Security Expression所授权

流程

下图显示了用户注册,登录,授权的流程:

在这里插入图片描述
下图显示了更新token的流程:

在这里插入图片描述

参考

  • https://www.bezkoder.com/spring-boot-jwt-authentication
  • https://www.bezkoder.com/spring-boot-jwt-mysql-spring-security-architecture
  • https://github.com/bezkoder/spring-boot-spring-security-jwt-authentication

附(流程的细化,未完)

接收HTTP请求
当一个HTTP请求到来时(无论从浏览器,web service客户端,HttpInvoker或者一个AJAX程序,Spring并不关心),它会经由一个filter链来做验证和授权。

过滤请求
我们添加了JwtAuthTokenFilter(扩展了Spring的OncePerRequestFilter抽象类)到filter链里。

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

JwtAuthTokenFilter使用JwtProvider来校验token:

class JwtAuthTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtProvider tokenProvider;

    @Override
    protected void doFilterInternal(...) {
        String jwt = getJwt(request);
        if (jwt!=null && tokenProvider.validateJwtToken(jwt)) {
            ...
        }
        filterChain.doFilter(request, response);
    }
}

现在分两种情况:

  • Login/SignUp:非保护API -> 用AuthenticationManager来验证Login请求,如果发生错误,用AuthenticationEntryPoint来处理AuthenticationException。
  • 受保护的资源:
    • JWT token为空/无效 -> 如果出现验证错误,用AuthenticationEntryPoint来处理AuthenticationException。
    • JWT token有效 -> 从token获取User信息,然后创建AuthenticationToken。

从token创建AuthenticationToken
JwtAuthTokenFilter使用JwtProvider从接收到的token解析出username/password,然后基于解析出来的数据,JwtAuthTokenFilter将会:

  • 创建一个AuthenticationToken(实现了Authentication)
  • 将该AuthenticationToken作为Authentication对象,并将其存储在SecurityContext,为将来的filter所用(例如Authorization filter)。
// extract user information
String username = tokenProvider.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);

// create AuthenticationToken
UsernamePasswordAuthenticationToken authentication
        = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
举报

相关推荐

0 条评论