Spring security 在前端使用AWS congnito身份验证实现Spring安全性

Spring security 在前端使用AWS congnito身份验证实现Spring安全性,spring-security,amazon-cognito,Spring Security,Amazon Cognito,我将我的申请分为两部分: 前端:Vue js并与AWS congnito连接以实现登录功能(电子邮件/pw或谷歌社交登录) 后端:Spring boot Restful。存储在数据库中的用户信息(来自congnito的唯一id作为主键。) 我的身份验证流程 用户已重定向到congnito并登录。congnito将返回唯一的id和JWT 前端将唯一id和JWT传递给后端控制器 后端验证JWT并从数据库返回用户信息 我的问题是: 在前端进行身份验证并将数据传递到后端以实现spring安全性,这

我将我的申请分为两部分:

  • 前端:Vue js并与AWS congnito连接以实现登录功能(电子邮件/pw或谷歌社交登录)
  • 后端:Spring boot Restful。存储在数据库中的用户信息(来自congnito的唯一id作为主键。)
我的身份验证流程

  • 用户已重定向到congnito并登录。congnito将返回唯一的id和JWT
  • 前端将唯一id和JWT传递给后端控制器
  • 后端验证JWT并从数据库返回用户信息
  • 我的问题是:

    • 在前端进行身份验证并将数据传递到后端以实现spring安全性,这是一种糟糕的做法吗?如果是的话,我可以有什么建议来改变我的实施流程吗
    • 要调用AuthenticationProvider.authenticate,需要一个由用户名(在我的例子中,是来自cognito的唯一id)和密码(UsernamePasswordAuthenticationToken)组成的身份验证。是否有只设置用户名的实现?或者可以将密码设置为空字符串
    //控制器
    公共字符串登录(HttpServletRequest-req,字符串cognitoId,字符串jwt){
    //使用AWS检查JWT
    if(!AwsJwtChecker(cognitoId,jwt))
    返回createErrorResponseJson(“无效jwt”);
    UsernamePasswordAuthenticationTokenAuthreq
    =新用户名PasswordAuthenticationToken(cognitoId,“”);
    Authentication auth=authManager.authenticate(authReq);
    SecurityContext sc=SecurityContextHolder.getContext();
    sc.setAuthentication(auth);
    HttpSession session=req.getSession(true);
    setAttribute(SPRING\u SECURITY\u CONTEXT\u KEY,sc);
    MyUser=userRepository.selectUserByCognitoId(cognitoId);
    返回CreateLoginSAccessResponse(用户);
    }
    //网络配置
    @组成部分
    公共类CustomAuthenticationProvider实现AuthenticationProvider{
    @凌驾
    公共身份验证(身份验证)引发AuthenticationException{
    字符串cognitoId=authentication.getName();
    //检查数据库中是否存在用户
    MyUser=userRepository.selectUserByCognitoId(cognitoId);
    如果(用户!=null){
    返回新的用户名PasswordAuthenticationToken(用户名,“,user.getRoles());
    }否则{
    抛出新的BadCredentialsException(“身份验证失败”);
    }
    }
    @凌驾
    公共布尔支持(ClassaClass){
    返回aClass.equals(UsernamePasswordAuthenticationToken.class);
    }
    }
    
    在前端进行身份验证并将数据传递到后端以实现spring安全性,这是一种糟糕的做法吗?如果是的话,我可以有什么建议来改变我的实施流程吗

    不,事实上这是最好的做法。JWT正是为了这个目的:您可以存储关于用户的信息,并且由于令牌的签名,您可以确定信息是可信的

    您没有描述在数据库中保存的内容,但从我的角度来看,您混合了两种身份验证方法。虽然这不是禁止的,但可能是不必要的。你有没有分析过你的代币?令牌中有许多关于用户的信息,可以添加更多信息

    Cognito在某些方面是有限的,比如组的数量,但对于一个基本的应用程序来说,它可能就足够了。它有一个很好的API来管理应用程序中的用户,比如添加组或设置属性

    您没有描述如何处理随3)返回的信息。Vue也可以使用jwt中存储的信息来显示用户名或类似的内容。您可以使用库解码令牌,例如,并获得包含所有信息的对象

    要调用AuthenticationProvider。请验证

    话虽如此,我对您的第二个问题的回答是:您不需要登录方法中的整个身份验证部分

    // controller
    public String login(HttpServletRequest req, String cognitoId, String jwt) { 
        // check JWT with AWS
        if(!AwsJwtChecker(cognitoId, jwt))
            return createErrorResponseJson("invalid jwt");
    
        return userRepository.selectUserByCognitoId(cognitoId);
    }
    
    这应该足够了,因为您已经验证了令牌。无需再次验证用户。正确设置spring security后,jwt将自动在SecurityContext中设置

    我看到您的实现存在的问题是,任何人都可以发送有效的jwt和随机cognitoId,并从数据库接收用户信息。因此,最好解析jwt并使用jwt中的一些内容(如用户名)作为数据库中的标识符。无法操纵令牌,否则验证将失败

    public String login(String jwt) { 
        // check JWT with AWS
        if(!AwsJwtChecker(jwt))
            return createErrorResponseJson("invalid jwt");
    
        String identifier = getIdentifier(jwt);
    
        return userRepository.selectUserByIdentifier(identifier);
    }
    

    感谢Shadog,您的回答给了我一个如何更新实现的想法。正如你提到的,cognito有局限性,从我的理解来看,它只允许存储用户的一些基本信息,如出生日期、姓名或角色等。如果我的应用程序想要根据用户信息定制我的页面,cognito可能对我来说不够。因此,我选择与我自己的数据库集成。请您也让我了解更多关于“当spring security设置正确时,jwt将自动在SecurityContext中设置”的信息?我想使用spring安全性的原因是,例如,我想阻止使用@Roles调用管理API的正常操作。虽然Cognito也允许我设置用户的组或角色,但前端的检查不是保存,每次获得一个新的JWT可能也不是一个好主意,因为它会降低性能。在这里,我介绍了如何与她一起设置spring security 5:您需要更多信息吗?主要区别在于,您在
    授权
    头中的每个请求中发送令牌。它保存在前端应用程序状态中,您可以通过JavaScript从localStorage.get(“access_token”)(或者是“id_token”)中获取它?有了这个,你不需要每次都获得一个新的令牌,只要它是有效的,它就存储在那里。对不起,我还有一个问题。根据你另一篇文章中的答案,它如何检查jwt是否过期?spring安全性是否检查授权
    public String login(String jwt) { 
        // check JWT with AWS
        if(!AwsJwtChecker(jwt))
            return createErrorResponseJson("invalid jwt");
    
        String identifier = getIdentifier(jwt);
    
        return userRepository.selectUserByIdentifier(identifier);
    }