Проблемы реализации Apache Shiro и JWT при использовании разных секретов для пользователя - PullRequest
0 голосов
/ 27 августа 2018

Я работаю над приложением, в котором реализован Java Restful Backend Sercvice с аутентификацией Apache Shiro. Теперь я могу заставить пользователя зарегистрироваться и успешно войти в систему, используя пароль и солидную базу данных. Теперь я хочу улучшить это, добавив аутентификацию JWT.

Сценарий будет:

  1. Пользователь пытается войти в систему, используя имя пользователя и пароль
  2. После успешной отправки учетных данных и входа пользователя в систему shiro серверная часть создает токен jwt и отправляет его обратно клиенту
  3. В каждом новом запросе клиент отправляет токен jwt, полученный на предыдущем шаге, на сервер для аутентификации.
  4. Shiro Filter проверяет токен, содержащийся в запросе. Если действительный процесс продолжается, в противном случае верните сообщение об ошибке.

Для реализации этой функции я следовал: Веб-токен JSON с Apache Shiro

Вся работа выполняется JWTVerifyingFilter:

public class JWTVerifyingFilter extends AccessControlFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse arg1, Object arg2) throws Exception {
        boolean accessAllowed = false;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String jwt = httpRequest.getHeader("Authorization");
        if (jwt == null || !jwt.startsWith("Bearer ")) {
            return accessAllowed;
        }
        jwt = jwt.substring(jwt.indexOf(" "));
        String username = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary("secret"))
                .parseClaimsJws(jwt).getBody().getSubject();
        String subjectName = (String) SecurityUtils.getSubject().getPrincipal();
        if (username.equals(subjectName)) {
            accessAllowed = true;
        }
        return accessAllowed;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest arg0, ServletResponse arg1) throws Exception {
        HttpServletResponse response = (HttpServletResponse) arg1;
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        return false;
    }

}

Однако я сталкиваюсь со следующими проблемами:

Как вы можете видеть, секрет, используемый для генерации подписи jwt, является одинаковым 'secret' для всех пользователей. В каждом примере, который я нашел относительно генерации подписи токена jwt и аутентификации, они используют один и тот же секрет для всех пользователей при генерации подписи jwt.

В моей реализации я хотел бы использовать соль, хранящуюся на пользователя в базе данных, чтобы сгенерировать подпись jwt, а затем проверить ее. Поэтому при создании jwt для пользователя я использую следующую функцию, которая использует соль пользователя для создания подписи jwt.

    public class JWTProvider {

    private JWTProvider() {

    }

    public static String getJWTToken(User user) {

        System.out.println("JWT Provider FIRED");
        SignatureAlgorithm sigAlg = SignatureAlgorithm.HS512;

        byte[] apiKeySecretBytes = Base64.decode(user.getSalt());

        Key signingKey = new SecretKeySpec(apiKeySecretBytes, sigAlg.getJcaName());
        Date date = new Date();
        JwtBuilder builder = Jwts.builder()
                .setSubject(user.getUsername())
                .claim("FirstName", user.getFirstName())
                .claim("LastName", user.getLastName())
                .setIssuedAt(date)
                .setExpiration(new Date(date.getTime() +  24 * 60 * 60 * 1000)) //Expires in One Days
                .signWith(sigAlg, signingKey);

        System.out.println("Generated JWT: " + builder.compact());
        return builder.compact();
    }

    }

В моем случае, для проверки подписи jwt мне нужно получить соль для каждого пользователя. Итак, я изменил реализацию JWTVerifyingFilter следующим образом:

public class JWTVerifyingFilter extends AccessControlFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
        System.out.println("JWT Verifier Fired.....");
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String jwt = httpRequest.getHeader("Authorization");
        if (jwt == null || !jwt.startsWith("Bearer ")) {
            System.out.println("DEn e brika prama: ");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        System.out.println("Found Something");
        jwt = jwt.substring(jwt.indexOf(" "));
        System.out.println("JWT: " + jwt);

        User user = (User) SecurityUtils.getSubject().getPrincipal();
        System.out.println("MMMMMMMMMMMMMM " + user.getUsername() + "jhhhhhhhhhhhhhh" + user.getSalt());

        String subjectName = ((User) SecurityUtils.getSubject()).getPrincipal();
        System.out.println("Subject: " + subjectName);
        String username = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary("secret"))
                .parseClaimsJws(jwt).getBody().getSubject();
        System.out.println("UserNAeme: " + username);
        System.out.println("Subject: " + subjectName);
        if (username.equals(subjectName)) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        return false;
    }
}

Однако при вызове Apache Shiro SecurityUtils.getSubject() возвращает объект null . Я попытался реализовать класс Singleton, чтобы всегда получать один и тот же объект из SecurityUtils (если есть один объект, вернуть его, иначе вызвать SecurityUtils.getSubject ()), но безуспешно. В этой более поздней реализации только один пользователь может войти в систему. Используя другой браузер, пользователь, о котором сообщалось в бэкэнде, уже вошел в систему с учетными данными пользователя, ранее зарегистрированного в другом браузере.

Вопросы:

  1. Можно ли использовать один и тот же секрет в аутентификации jwt для всех пользователей ??
  2. Где apache shiro хранит информацию о вошедших в систему пользователях и как я могу получить к ним доступ из java-сервиса restful backend ??
  3. Какой-нибудь полный пример использования Apache Shiro различных секретных фраз jwt для каждого пользователя Каков наилучший способ реализовать это.
  4. Нужно ли мне хранить в памяти, например, redis, чтобы аутентифицированные пользователи сохраняли свои соли при входе в систему и получали их оттуда при последующих запросах ??

Заранее спасибо за любые ответы.

1 Ответ

0 голосов
/ 27 августа 2018

Проблема была в моем shiro.ini

Я включил опцию

securityManager.sessionManager.sessionIdCookieEnabled = false

Я прокомментировал это, и все работает отлично.Теперь у меня есть доступ к теме через Широ SecurityUtils.getSubject();

...