Реализовать функцию проверки входа в систему с помощью токена JWT.

Spring Boot

предисловие

Перед праздником я сделал несколько небольших проектов + дизайн курса, во всех из которых использовались токены для проверки входа и оценки разрешений, однако Гусь и друзья в той же группе также впервые столкнулись с токенами, поэтому они все запутался.(xjbx) закончил писать фронтенд и бэкенд логику проверки входа (я пишу фронтенд, а мелкие партнеры в той же группе пишут бэкенд). Сегодня у меня есть время внимательно изучить реализацию SpringBoot аутентификации по токену и взаимодействия с фронтендом, я наступил на много ям и записал это сюда.

бэкэнд реализация

  • Во-первых, вам нужно импортировать пакет jwt, соответствующий файл pom.xml выглядит следующим образом:
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.5.0</version>
</dependency>
  • Затем начните писать класс TokenUtil, сначала определите время истечения срока действия токена и закрытый ключ.
private static final long EXPIRE_TIME = 15 * 60 * 1000;
private static final String TOKEN_SECRET = "thefirsttoken123";
  • Реализуйте метод подписи: Здесь нельзя использовать пароли для шифрования, это небезопасно, но так написано для моего собственного небольшого демо.
/**
 * 生成签名,15分钟过期
 * @param **username**
* @param **password**
* @return
 */
public static String sign(String username, String password) {
    try {
        // 设置过期时间
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        // 私钥和加密算法
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
        // 设置头部信息
        Map<String, Object> header = new HashMap<>(2);
        header.put("Type", "Jwt");
        header.put("alg", "HS256");
        // 返回token字符串
        return JWT.create()
                .withHeader(header)
                .withClaim("loginName", username)
                .withClaim("pwd", password)
                .withExpiresAt(date)
                .sign(algorithm);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
  • Метод проверки для реализации токена:
/**
 * 检验token是否正确
 * @param **token**
* @return
 */
public static boolean verify(String token){
    try {
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(token);
        return true;
    } catch (Exception e){
        return false;
    }
}

На данный момент класс инструмента написан!

  • Метод уровня контроллера входа Здесь получается тело запроса, отправленное фронтендом, вынимаются имя пользователя и пароль, и если они сравниваются с базой данных, токен выдается и возвращается на фронтенд. (Результат ответа API не был инкапсулирован, он выглядит немного запутанным, хе-хе)
@PostMapping(value = "/login")
public Map<String, Object> login(@RequestBody SysUser sysUser){
    Map<String, Object> map = new HashMap<>();
    String username = sysUser.getUsername();
    String password = sysUser.getPassword();
    if (sysUserService.login(username, password)){
        String token = TokenUtil.sign(username,password);
        if (token != null){
            map.put("code", "10000");
            map.put("message","认证成功");
            map.put("token", token);
            return map;
        }
    }
    map.put("code", "00000");
    map.put("message","认证失败");
    return map;
}

Теперь функция сервера по выдаче токена клиенту практически реализована. Так как же клиент применяет токен к будущим запросам и как сервер идентифицирует токен?

  • Внедрение пользовательских перехватчиков на стороне сервера
/**
 * 自定义token拦截器
 */
@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        response.setCharacterEncoding("utf-8");
        String token = request.getHeader("admin-token");
        if (token != null){
            boolean result = TokenUtil.verify(token);
            if(result){
                System.out.println("通过拦截器");
                return true;
            }
        }
        System.out.println("认证失败");
        response.getWriter().write("50000");
        return false;
    }
}

TokenInterceptor реализует интерфейс HandlerInterceptor и переписывает метод preHandle, этот метод запускает выполнение перед каждым запросом и берет токен из заголовка запроса, здесь мы унифицируем ключ для хранения токена как admin-token, проверка пройдена, выпущена, подтверждён Если не пройден, вернуть информацию об ошибке аутентификации.есть дыра, из-за использования axios, каждый раз, когда интерфейс отправляет запрос, сначала будет отправлен пре-запрос, то есть RequestMethodOPTIONSНе наш общий get, post и т.д. (для пояснения ВАРИАНТОВ можно погуглить). Все здесь нам нужно принять решение, если метод запроса является OPTIONS, он вернется напрямую.

  • Настроить перехватчик Запросы к интерфейсу входа не перехватываются
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    private TokenInterceptor tokenInterceptor;

    public InterceptorConfig(TokenInterceptor tokenInterceptor) {
        this.tokenInterceptor = tokenInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        String sysUserLogin = "/api/sysUser/login";
        excludePath.add(sysUserLogin);
        registry.addInterceptor(tokenInterceptor).excludePathPatterns(excludePath);
    }
}
  • Токен синтаксического анализа сервера Теперь, чтобы позже выполнять связанные запросы на основе токена, нам нужно расшифровать токен и извлечь ранее зашифрованное имя пользователя. Затем вы можете с радостью добавлять, удалять, проверять и изменять ~
/**
 * 从token中获取username信息
 * @param **token**
* @return
 */
public static String getUserName(String token){
    try {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim("loginName").asString();
    } catch (JWTDecodeException e){
        e.printStackTrace();
        return null;
    }
}

Интерфейсная реализация

Внешний интерфейс использует vue+axios, в основном для реализации переупаковки axios. Соответствующий код выглядит следующим образом Общая логика заключается в том, что если токен уже существует в vuex, он будет помещен в заголовок запроса и отправлен на сервер.

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API // api 的 base_url
})

// request拦截器
service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      config.headers['admin-token'] = getToken() // 让每个请求携带自定义token
    }
    return config
  },
  error => {
    // 出错
    console.log(error)
    Promise.reject(error)
  }
)

Эпилог

Вау, закончил писать первую статью о Наггетс, хи-хи, продолжай усердно работать! ! ! .

Вчера, сегодня, завтра код изо дня в день без остановок,
Год за годом, год за годом, я один.
Хэнчжуань: это жизнь