当前位置: 首页 > 知识库问答 >
问题:

Apache Shiro和JWT在每个用户使用不同秘密时的实现问题

齐承运
2023-03-14
  1. 用户尝试使用用户名和密码登录
  2. 成功发送凭据并且shiro登录用户后,后端生成一个jwt令牌并将其发送回客户端
  3. 在每个新请求中,客户机将在上一步中接收到的jwt令牌发送到服务器以进行身份验证。
  4. Shiro筛选器检查请求中包含的令牌。如果处理有效,则else返回错误消息。

为了实现这个功能,我遵循了以下操作:使用Apache Shiro的JSON Web令牌

整个作业由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;
    }

}

在我的实现中,我希望使用数据库中每个用户存储的salt来生成jwt签名,然后验证它。因此,在生成每个用户的jwt时,我使用以下函数,该函数使用用户salt创建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签名,我需要获取每个用户的salt。因此,我将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()调用时返回一个空对象。我试图实现一个单例类,以从SecurityUtils中获取始终相同的主题(如果有一个主题,则返回它,否则调用SecurityUtils.getSubject())但没有成功。在后面的实现中,只有一个用户可以登录到系统。使用另一个浏览器,后端报告的用户已经使用以前由其他浏览器登录的用户的凭据登录。

  1. 在jwt身份验证中对所有用户始终使用相同的秘密是否可以??
  2. apache shiro将登录用户的信息保存在哪里,以及如何从restful后端java服务访问他们?
  3. Apache Shiro是否为每个用户使用不同的jwt秘密短语的完整示例?什么是最好的方法来实现这一点。
  4. 我是否需要像redis这样的内存存储,以便在登录时将经过身份验证的用户保留在salt中,并在随后的请求中从那里获取salt??

谢谢你的回答。

共有1个答案

赫连骏
2023-03-14

问题出在我的shiro.ini

我已经启用了该选项

securityManager.sessionManager.sessionIdCookieEnabled = false

我把它删掉了,一切都很好。现在我可以通过Shiro的SecurityUtils.getSubject();访问subject

 类似资料:
  • 我有麻烦管理詹金斯的秘密密码。我希望密码不显示在日志上,但我尝试了两种方法没有成功: 第一次尝试 我尝试使用全局凭据(不受限制)设置用户和pwd,如下所示: 我执行了: 但我可以在日志中看到,用户的写入是正确的,但pwd的写入方式与它的路径相同: 在我的自动测试中,也是通过输入完整路径而不是变量的值。 第二次尝试 我试着用这样的密文: 但控制台日志上显示的是原样的密码。 有人能帮我吗? 先谢谢你。

  • 问题内容: 我有一个Web应用程序,并且我想为每个用户使用不同的日志,因此我可以了解该用户在系统上所做的事情的“历史记录”。 这是我到目前为止所拥有的: 问题是,作为Web应用程序,它是多线程的,所以AFAIK我不能一直使用并根据我要登录的用户来更改附加程序。我认为我应该为每个用户创建不同的内容,但这是正确的吗? 问题答案: 尝试切换到logback(log4j的后继者)。它带有一个Sifting

  • 问题内容: 我有两个应用程序: server ( REST API Server) node js Express jsonwebtokens express-jwt mongoose client (Portable Front-end) bootstrap Angular JS local-storage angular-facebook angular-jwt 稍后,客户端应用程序将使用ph

  • 在我的应用程序中,我必须存储其用户的非常敏感的数据,例如其他第三方服务的各种密码(用户填写表格,向我们提供第三方服务的登录名和密码) 该应用程序的目标是使用从100多个输入生成的powershell脚本来设置其他复杂系统。需要将用户工作保存为草稿,这就是为什么我需要以某种方式加密敏感字段。 我读了很多关于Azure Key Vault的文章,每当我读到有关机密的文章时,它们似乎被描述为保存应用程序

  • 本文向大家介绍使用mongoose和bcrypt实现用户密码加密的示例,包括了使用mongoose和bcrypt实现用户密码加密的示例的使用技巧和注意事项,需要的朋友参考一下 前面的话 最近在做的个人项目中,需要对密码进行加密保存,对该操作的详细步骤记录如下 介绍 关于mongoose已经写过博客就不再赘述,下面主要介绍bcrypt bcrypt是一个由两个外国人根据Blowfish加密算法所设计