Проблемы реализации Apache Shiro и JWT при использовании разных секретов для каждого пользователя

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

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

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

Чтобы реализовать эту функциональность, я использовал: JSON Web Token с 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, является одним и тем же «секретом» для всех пользователей. В каждом примере, который я обнаружил относительно генерации и аутентификации подписи токена 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;
    }
}

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

Вопросов:

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

Заранее благодарим за любые ответы.


person mixtou    schedule 27.08.2018    source источник


Ответы (1)


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

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

securityManager.sessionManager.sessionIdCookieEnabled = false

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

person mixtou    schedule 27.08.2018