JWT 的认证流程

1、用户使用用户名和密码登录

2、服务器验证用户凭据

3、一旦凭据验证成功,服务器会创建一个 JWT 并返回给用户

4、用户将 JWT 存储在本地,通常是 cookie 或者 localStorage

5、用户每次向服务器发送请求时,都会在请求头中包含 JWT

6、服务器会验证 JWT ,如果验证通过,则会处理请求,返回数据

7、如果用户注销或者 JWT 过期,服务器会拒绝请求

以下是 JWT 在服务端的认证示例:

const jwt = require('jsonwebtoken');

const secret = 'your-secret-key';

// 用户登录
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // 验证用户名和密码
  const user = authenticate(username, password);
  if (user) {
    // 创建 JWT
    const token = jwt.sign({ id: user.id }, secret);
    // 将 JWT 发送给用户
    res.json({ token });
  } else {
    res.status(401).send('Invalid credentials');
  }
});

// 用户请求资源
app.get('/resource', (req, res) => {
  const token = req.headers['authorization'];
  // 验证 JWT
  jwt.verify(token, secret, (err, decoded) => {
    if (err) {
      res.status(401).send('Invalid token');
    } else {
      // 处理请求
      res.json({ resource: 'Here is your resource' });
    }
  });
});

### 使用 Spring Security 实现 JWT 认证流程 #### 一、JWT 认证流程概述 JSON Web Token (JWT) 是一种开放标准 RFC 7519,用于在网络应用环境间安全地传输信息。它通过加密签名来验证数据的真实性,并可以附加一定的安全性。在基于 Spring Security 的项目中,JWT 被用来替代传统的会话管理方式[^1]。 典型的 JWT 流程如下: - 用户发送用户名和密码到服务器进行身份验证。 - 如果验证成功,则返回一个由服务器生成并签名的 JWT 给客户端。 - 客户端将此令牌存储起来(通常保存在本地存储或 Cookie 中),并在后续请求头中的 `Authorization` 字段附带该令牌。 - 每次接收到带有令牌的请求时,服务器都会解析并验证其有效性以及签名校验。 #### 二、Spring Security 基本配置 为了集成 Spring Security 和 JWT,需要创建几个核心组件: ##### 1. 配置类 编写一个继承自 `WebSecurityConfigurerAdapter` 的 Java Config 类来进行全局的安全设置。在此过程中需指定哪些 URL 不受保护可以直接访问(如登录接口)、其余路径则强制要求提供有效的 token 才能继续操作[^3]。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final JwtTokenProvider jwtTokenProvider; public SecurityConfig(JwtTokenProvider jwtTokenProvider){ this.jwtTokenProvider = jwtTokenProvider; } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/auth/login").permitAll() // 登录接口无需认证 .anyRequest().authenticated(); // 其他所有请求都需要经过认证 http.apply(new JwtTokenFilterConfigurer(jwtTokenProvider)); } } ``` ##### 2. 自定义过滤器 构建专门处理 HTTP 请求头部携带的 Bearer Tokens 的 Filter 来提取用户的凭据信息。 ```java public class JwtTokenAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider jwtTokenProvider; public JwtTokenAuthenticationFilter(JwtTokenProvider jwtTokenProvider){ this.jwtTokenProvider = jwtTokenProvider; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException{ String token = resolveTokenFromRequest(request); if(token != null && jwtTokenProvider.validateToken(token)){ Authentication auth = jwtTokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request,response); } private String resolveTokenFromRequest(HttpServletRequest request){ String bearerToken = request.getHeader("Authorization"); if(bearerToken!=null&&bearerToken.startsWith("Bearer ")){ return bearerToken.substring(7,bearerToken.length()); } return null; } } ``` #### 三、JWT 生成与解析 当用户提交正确的凭证后,应该生成一个新的 JSON Web Token 并将其作为响应的一部分返回给前端应用程序。这个过程涉及到算法的选择、密钥管理和实际编码逻辑等方面的内容[^2]。 下面展示了一个简单的例子说明如何利用 JJWT 库制作这样的 tokens: ```java @Service public class JwtTokenProvider { private static final long EXPIRATION_TIME = 864_000_000; // 有效期一天 private static final String SECRET_KEY = "SecretKeyToSignOurTokens"; public String createToken(String username,List<String> roles){ Claims claims = Jwts.claims(); claims.put("roles",roles); Date nowDate=new Date(); return Jwts.builder() .setClaims(claims) .setSubject(username) .setIssuedAt(nowDate) .setExpiration(new Date(System.currentTimeMillis()+EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512,SECRET_KEY.getBytes()) .compact(); } public boolean validateToken(String token){ try{ Jws<Claims> jws=Jwts.parserBuilder(). setSigningKey(SECRET_KEY.getBytes()).build().parseClaimsJws(token); return !jws.getBody().getExpiration().before(new Date()); }catch(Exception e){return false;} } public Authentication getAuthentication(String token){ Claims body=Jwts.parserBuilder(). setSigningKey(SECRET_KEY.getBytes()).build().parseClaimsJws(token).getBody(); List<GrantedAuthority> authorities= ((List<?>)body.get("roles")).stream().map(role->new SimpleGrantedAuthority((String)role)).collect(Collectors.toList()); User principal=new User(body.getSubject(), "",authorities ); return new UsernamePasswordAuthenticationToken(principal,null ,authorities ); } } ``` #### 四、实现用户登录和认证 最后一步就是开发具体的控制器动作以支持上述提到的功能需求——即允许外部调用者传入他们的账户详情从而换取相应的 access_token 。这里给出了一种可能的设计方案: ```java @RestController @RequestMapping("/auth") public class AuthController { private final UserService userService; private final PasswordEncoder passwordEncoder; private final JwtTokenProvider jwtTokenProvider; public AuthController(UserService userService,PasswordEncoder passwordEncoder,JwtTokenProvider jwtTokenProvider ){ this.userService=userService; this.passwordEncoder=passwordEncoder; this.jwtTokenProvider=jwtTokenProvider ; } @PostMapping("/login") public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest){ Optional<UserEntity> userOptional=this.userService.findByUsername(loginRequest.getUsername()); if(!userOptional.isPresent()){ throw new BadCredentialsException("Invalid Credentials!"); } UserEntity user=userOptional.get(); if(this.passwordEncoder.matches(loginRequest.getPassword(),user.getPassword())){ List<String> roles=user.getRoles().stream().map(Role::getName).collect(Collectors.toList()); String accessToken=this.jwtTokenProvider.createToken(user.getUsername(),roles); Map<Object,Object> model=new HashMap<>(); model.put("access_token",accessToken); return ResponseEntity.ok(model); }else{ throw new BadCredentialsException("Invalid Credentials!"); } } } class LoginRequest{ private String username; private String password; // Getters & Setters omitted for brevity. } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值