Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring Boot(二)——JWT #243

Open
soapgu opened this issue May 11, 2024 · 0 comments
Open

Spring Boot(二)——JWT #243

soapgu opened this issue May 11, 2024 · 0 comments

Comments

@soapgu
Copy link
Owner

soapgu commented May 11, 2024

  • 什么是JWT

全称为JSON Web Token,是现在客户端作为登陆凭证的主流

  • JWT 结构

一般JWT表达为这样

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTZXJ2aWNlIiwiYXVkIjoiQVBQIiwidXNlcl9pZCI6IjEyMyIsImlhdCI6MTcxNTQyMDM0NSwiZXhwIjoxNzE1NDYzNTQ1fQ.xYc2xFt4Y7jvqyHhSd6waoz_puUB_mE4U7fM8guAPZU
一共分为三个部分

Header(头部)
Payload(负载)
Signature(签名

拼在一起就是这样

Header.Payload.Signature

在客户端使用一般是加到Header里面去

Authorization: Bearer <token>
  • JWT在SpringBoot中实现

  1. 增加依赖
         <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>4.4.0</version>
        </dependency>
  1. 创建JWT Service
public interface JWTService {
    String createToken(String userId);
    String verifyToken(String token);
}
  1. 实现JWT Service
@Service
public class JWTServiceImpl implements JWTService{
    public static final String SECRET = "SHGBIT";
    private final Log log = LogFactory.getLog(JWTServiceImpl.class);

    @Override
    public String createToken(String userId) {
        Date iatDate = new Date();
        // expire time
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.HOUR, 12);
        Date expiresDate = nowTime.getTime();

        // header Map
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        return JWT.create().withHeader(map) // header
                .withClaim("iss", "Service") // payload
                .withClaim("aud", "APP")
                .withClaim("user_id", userId)
                .withIssuedAt(iatDate) // sign time
                .withExpiresAt(expiresDate) // expire time
                .sign(Algorithm.HMAC256(SECRET));
    }

    @Override
    public String verifyToken(String token) {
        DecodedJWT jwt = null;
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            jwt = verifier.verify(token);
        } catch (Exception e) {
            log.error("verifyToken error",e);
            return null;
        }
        return jwt.getClaims().get("user_id").asString();
    }
}

分别实现了JWT和创建和解析

  • 这里JWT的过期时间设为2小时后
  • 把user_id作为自定义字段加入Payload
  1. 登陆实现
@RestController
public class OAuthController {

    @Autowired
    JWTService jwtService;
    @PostMapping("oauth/token")
    public ResponseEntity<Map<String,String>> login(@RequestBody UserAuth auth){
        if( auth.isVaild() ){
            String token = jwtService.createToken( auth.getUserId() );
            Map<String, String> map = new HashMap<>();
            map.put("access_token",token);
            return ResponseEntity.ok( map );
        }
        return ResponseEntity.badRequest().build();
    }
}

这里把生成的jwt作为access_token传递给客户端

  1. 验证jwt

那么面对多个路由要怎么分别验证?这里使用拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor( jwtInterceptor )
                .addPathPatterns("/user/**");
    }
}

这里把通配路由/user/**都进行了拦截

@Component
public class JwtInterceptor implements HandlerInterceptor {
    private final Log log = LogFactory.getLog(InterceptorConfig.class);

    @Autowired
    private JWTService jwtService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("JwtInterceptor preHandle");
        Map<String, String> pathVariables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String token = request.getHeader("Authorization");
        if( token != null && !token.isEmpty() ) {
            String userId = jwtService.verifyToken(token.replace("Bearer ", ""));
            if( userId != null && !userId.isEmpty() ) {
                log.info("Jwt get userId:" + userId);
                log.info("Path userId:" + pathVariables.get("userId"));
                if(userId.equals(pathVariables.get("userId"))){
                    return true;
                }
            }
        }
        response.setStatus(401);
        return false;
    }
}

这里把路由中path的的userId和jwt的userId是否一致

soapgu added a commit that referenced this issue May 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant