Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/29.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何为需要验证由第三方和用户生成的JWT令牌的rest api配置spring http安全性;你没有密码吗?_Java_Angular_Spring_Security_Jwt - Fatal编程技术网

Java 如何为需要验证由第三方和用户生成的JWT令牌的rest api配置spring http安全性;你没有密码吗?

Java 如何为需要验证由第三方和用户生成的JWT令牌的rest api配置spring http安全性;你没有密码吗?,java,angular,spring,security,jwt,Java,Angular,Spring,Security,Jwt,我想保护一个由Angular应用程序使用的Spring Boot REST API。任何用户和其他应用都不应访问此API 我无法控制这个angular应用程序,但有人告诉我,用户的实际身份验证正在angular应用程序上进行,该应用程序在对其用户进行身份验证后设置并发送JWT。由于angular应用程序和我的API部署在同一个域上,因此我确实能够访问JWT,该请求作为cookie发送。我还能够成功地从中提取用户名 我现在必须使用JWT用户名和与之关联的角色对请求进行身份验证和授权,以限制对某些端

我想保护一个由Angular应用程序使用的Spring Boot REST API。任何用户和其他应用都不应访问此API

我无法控制这个angular应用程序,但有人告诉我,用户的实际身份验证正在angular应用程序上进行,该应用程序在对其用户进行身份验证后设置并发送JWT。由于angular应用程序和我的API部署在同一个域上,因此我确实能够访问JWT,该请求作为cookie发送。我还能够成功地从中提取用户名

我现在必须使用JWT用户名和与之关联的角色对请求进行身份验证和授权,以限制对某些端点的访问。为了做到这一点,我已经获得了访问几个包含所需信息的数据库视图的权限,但是我没有获得任何类型的密码信息。我无法控制该数据库中的视图或任何其他内容。显然,如果在这些视图中找不到令牌中的用户名,则该用户是未经授权的。角色也是如此

我的问题是如何配置
websecurityConfigureAdapter
UserDetails

无论我尝试什么,当从控制器代码调用时,我要么立即得到401 unauthorized,要么返回
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
,而不是
UserDetails
对象,即使它设置正确

这只是一个猜测,但它可能返回401,因为在我的
UserDetails
类中被重写的
getPassword()
方法返回一个空字符串(
return”“;
),但它应该返回什么呢,因为我在任何时候都不能访问任何类似于用户密码的内容

Web安全配置适配器:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        httpSecurity.cors().and().csrf().disable()
                     // I will eventually filter multiple endpoints by roles
                     // but at this point I just want it to work 
                    .authorizeRequests().anyRequest().authenticated().and() // I am guessing it fails here because of the password issue?
                    .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
用户详细信息:

public class MyUserDetails implements UserDetails
{
    private static final long serialVersionUID = 7371936004280236913L;

    private final MyUserDto MyUser;

    public MyUserDetails(MyUserDto MyUser)
    {
        this.MyUser = MyUser;
    }

    public MyUserDto getMyUser()
    { return MyUser; }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
    {
        List<MyUserAuthority> authorities = new ArrayList<MyUserAuthority>();
        for(MyRoleDto userRole : MyUser.getRoles())
            authorities.add(new MyUserAuthority(userRole));
        return authorities;
    }

    @Override
    public String getPassword()
    { return ""; } // What should this return?

    @Override
    public String getUsername()
    { return this.MyUser.getUserId(); }

    @Override
    public boolean isAccountNonExpired()
    { return true; }

    @Override
    public boolean isAccountNonLocked()
    { return true; }

    @Override
    public boolean isCredentialsNonExpired()
    { return true; }

    @Override
    public boolean isEnabled()
    { return true; }
}
公共类MyUserDetails实现UserDetails
{
私有静态最终长serialVersionUID=7371936004280236913L;
私有最终MyUserdToMyUser;
公共MyUserDetails(MyUserDto MyUser)
{
this.MyUser=MyUser;
}
public MyUserDto getMyUser()
{返回MyUser;}
@凌驾

公共集合好吧,既然有这么多的答案,就像java/spring boot一样,这是一种流行的、有文档记录的编程语言和框架,我决定只使用用户名作为密码

@Override
public String getPassword()
{
    // I am using BCryptPasswordEncoder but you may 
    // have to change to whatever you're using.
    return new BCryptPasswordEncoder().encode(this.getUsername()); 
}
请记住,您必须对其进行编码,因为密码提供程序希望对其进行编码。否则,提供程序会说密码不匹配


我不知道这是否是“最佳实践”,但它确实有效。

我认为
user1969903
已经回答了问题的解决方案。但是,我建议您在解决方案中添加一小撮盐

@覆盖
公共字符串getPassword()
{
//我正在使用BCryptPasswordEncoder,但您可以
//你必须改变你正在使用的东西。
返回新的BCryptPasswordEncoder().encode(this.getUsername()+someString);
}

someString
可以是您可以在您的环境中配置的内容,也可以是用户配置文件数据的一部分,如姓氏、电子邮件、zipcode等。这样,如果有人碰巧获得令牌,他们就不容易对其进行解码。

您的问题与设置或用户密码无关

@Override
public String getPassword()
{
    // I am using BCryptPasswordEncoder but you may 
    // have to change to whatever you're using.
    return new BCryptPasswordEncoder().encode(this.getUsername()); 
}
正如您所指出的,用户已经在前端通过身份验证,可能是通过与OAuth2客户端交互。因此,OAuth2客户端将为您的前端提供JWT令牌

在每次HTTP交互中,前端都会将此JWT令牌发送到后端

后端有两个职责:

  • 一方面,验证接收到的令牌是否有效(过期,如果它是数字签名的,则带有有效签名,来自可信来源,具有正确的访问群体,等等)
  • 另一方面,您需要识别与接收到的JWT令牌相对应的用户
对于第二个职责,您需要一些在JWT令牌中定义的所有属性中唯一标识用户的属性

此属性必须是用于从数据库中获取用户信息的属性

对于Spring安全性,您需要从
UserDetailsService
实现中的
loadByUserName
方法返回
UserDetails
接口的一些实现

如您所述,如果您能够获得一个
用户名
,只需创建一个
用户详细信息服务
实现,该实现使用该信息查询数据库中的视图以获取用户信息,并定义实现
用户详细信息
的某个类,而不注意密码,您就不需要了首先,您不需要再次对用户进行身份验证

UserDetailsService
实现将与Spring安全配置相关

您的用户已预验证,您需要预验证资料。请在
WebSecurityConfig
类中包含以下内容:

@覆盖
受保护的无效配置(AuthenticationManagerBuilder auth)引发异常{
UserDetailsByNameServiceWrapper=新的UserDetailsByNameServiceWrapper(userDetailsService);
PreAuthenticateAuthenticationProvider PreAuthenticationProvider=新的PreAuthenticateAuthenticationProvider();
setPreAuthenticatedUserDetailsService(包装器);
身份验证提供者(预授权提供者);
}
如果您的
JwtRequestFilter
如您所述是
OncePerRequestFilter
,则在验证JWT令牌并获得唯一用户标识符后,您需要将此信息提供给Spring Security
AuthenticationManager
,以获得对适当用户的引用<