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

“在SecurityContext中找不到身份验证对象”-在Spring Boot中的UserDetails服务设置

怀洛华
2023-03-14

在Spring Boot应用程序中,我有一个内存中的Spring Security设置。它可以按照期望工作。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
            .withUser("kevin").password("password1").roles("USER").and()
            .withUser("diana").password("password2").roles("USER", "ADMIN");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {

    http
            .httpBasic().and()
            .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/foos").hasRole("ADMIN")
            .antMatchers(HttpMethod.PUT, "/foos/**").hasRole("ADMIN")
            .antMatchers(HttpMethod.PATCH, "/foos/**").hasRole("ADMIN")
            .antMatchers(HttpMethod.DELETE, "/foos/**").hasRole("ADMIN")
            .and()
            .csrf().disable();
  }
}

现在,我用以下代码将其转换为基于数据库的方法。

@Entity
class Account {

  enum Role {ROLE_USER, ROLE_ADMIN}

  @Id
  @GeneratedValue
  private Long id;

  private String userName;

//  @JsonIgnore
  private String password;

  @ElementCollection(fetch = FetchType.EAGER)
  Set<Role> roles = new HashSet<>();
   ...
}

存储库:

@RepositoryRestResource
interface AccountRepository extends CrudRepository<Account, Long>{

  @PreAuthorize("hasRole('USER')")
  Optional<Account> findByUserName(@Param("userName") String userName);
}

UserDetailsService:

@Component
class MyUserDetailsService implements UserDetailsService {

  private AccountRepository accountRepository;

  MyUserDetailsService(AccountRepository accountRepository){
    this.accountRepository = accountRepository;
  }

  @Override
  public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
    Optional<Account> accountOptional = this.accountRepository.findByUserName(name);
    if(!accountOptional.isPresent())
        throw new UsernameNotFoundException(name);

    Account account = accountOptional.get();
    return new User(account.getUserName(), account.getPassword(),
        AuthorityUtils.createAuthorityList(account.getRoles().stream().map(Account.Role::name).toArray(String[]::new)));
  }
}

以及对WebSecurity配置适配器配置的修改:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  private MyUserDetailsService userDetailsService;

  SecurityConfiguration(MyUserDetailsService userDetailsService){
    this.userDetailsService = userDetailsService;
  }

  @Override
  public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService);  // <-- replacing the in-memory anthentication setup
  }
  ...
}

当我发送与内存版本相同的用户名和密码作为基本身份验证的请求时,我会得到401错误:

{
  "timestamp": 1489430818803,
  "status": 401,
  "error": "Unauthorized",
  "message": "An Authentication object was not found in the SecurityContext",
  "path": "/foos"
}

在阅读了一些相关文档和示例代码后,我看不出错误的原因。错误消息说用户不在Spring Security上下文中。userDetailsService(userDetailsService)中的AuthenticationManagerBuilder使用行应负责在SecurityContext中设置这些用户,不是吗?

Spring Boot版本是1.4.3。释放

共有3个答案

梁祯
2023-03-14

真正的问题在于UserDetailService。loadUseByUsername-您可以在这里调用:

Optional<Account> accountOptional = this.accountRepository.findByUserName(name);

并且它是在te授权过程中完成的。但是帐户库方法用PreAuthorize注释,并且只能从包含身份验证对象上下文中调用。

做一个简单的测试来理解它:

@RepositoryRestResource
interface AccountRepository extends CrudRepository<Account, Long>{

  @PreAuthorize("hasRole('USER')")
  Page<Account> findAll(Pageable pageable);

  Optional<Account> findByUserName(@Param("userName") String userName);
}

然后检查,您可以正确查询所有用户。唯一的问题是findByUserName方法的递归性质。

干杯

鲍永春
2023-03-14

一个问题是,您的存储库查找器被注释为@PreAuthorize("hasRole('USER')")。此方法在请求可以(完全)验证之前被调用,因此它永远无法通过检查,即使您通过了正确的凭据,也会引发异常。

另一点是,您真的想将您的用户帐户数据公开为ReST资源吗?目前,像curlhttp://localhost:8080/accounts这样的请求将返回您的所有用户数据作为JSON HAL响应,即使是匿名用户。

它会返回类似于:

{
  "_embedded" : {
    "accounts" : [ {
      "userName" : "admin",
      "password" : "admin",
      "roles" : [ "ROLE_USER", "ROLE_ADMIN" ],
      "_links" : {
    "self" : {
      "href" : "http://localhost:8080/accounts/1"
    },
    "account" : {
      "href" : "http://localhost:8080/accounts/1"
    }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/accounts"
    },
    "profile" : {
      "href" : "http://localhost:8080/profile/accounts"
    },
    "search" : {
      "href" : "http://localhost:8080/accounts/search"
    }
  }
}

所以@RepositoryRestResources在这里是危险的,如果您只想使用数据库表来验证您的请求,使用@Repository是最好的方法

有关详细信息,请参阅文档。

即使您按照代码的建议从响应中删除了密码(在您的帐户类中带有注释@JsonIgnore注释),您也会暴露与安全相关的信息,如用户名和角色。此外,“滥用”实体来塑造响应对象是一种不好的做法。最好使用投影。

如果你想保留你的/accountsendpoint,你还应该至少通过添加来保护它。蚂蚁匹配器(“/accounts/**”)。将角色(“管理员”)添加到您的配置中。

潘嘉佑
2023-03-14

crud存储库中删除此预授权注释。当您试图登录,但使用预授权注释时,会使用此方法,它希望用户登录。

  @PreAuthorize("hasRole('USER')")
  Optional<Account> findByUserName(@Param("userName") String userName);

我对websecurityconfigureadapter中的配置方法做了一些更改,您可能需要允许在没有登录权限的情况下访问配置方法中的登录和登录网址。

@Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeUrls()
        .antMatchers("/signup","/about").permitAll() // #4
        .antMatchers("/admin/**").hasRole("ADMIN") // #6
        .anyRequest().authenticated() // 7
        .and()
    .formLogin()  // #8
        .loginUrl("/login") // #9
        .permitAll(); // #5
  }

当需要管理员权限才能访问/foo/**此类URL时,请务必小心。这表明所有以foo开头的URL仅允许管理员使用。

 类似资料:
  • 我有一个使用spring security oauth2进行安全连接的项目。下面是我的spring配置文件。 Spring-安全.xml : 当我使用以下请求请求oauth访问令牌时,我将获得如下访问和刷新令牌。 请求是 回应是: 然后,当我使用下面的请求请求受保护的资源时,我收到“在SecurityContext中找不到身份验证对象”作为错误。 请求是 : 我使用“2.0.7.RELEASE”作

  • 问题内容: 我试图从成功登录后实现接口的类(Spring 3.2.2和Spring Security 3.2.0 M1)中调用受保护的方法。这是我以前的问题。 该应用程序在以下环境下运行。 Spring 3.2.2 Spring Security 3.2.0 JPA 2.0 JSF 2.1.9 MySQL 5.6.11 JDK-7u11 NetBeans 7.2.1 我已经将以下与Spring安全

  • 我是Spring Boot和Spring Security的新手,继承了一个使用它们的webapp项目。我们将把webapp迁移到新的部署环境中。我们要改变的事情之一是认证机制,以便它能在新环境中运行。同时,我想使用一些现有的PostMan测试来测试RESTendpoint,绕过安全性。基本上,我想暂时禁用安全。 我有一个提供全局方法级安全性的类: 我有多个控制器类,例如,一个类 如果我尝试运行邮

  • 我有一个实现Spring Security和Spring OAuth2安全的项目。当我请求访问令牌时,它工作得很好,但当我使用访问令牌请求资源时,我得到了“在SecurityContext中没有找到身份验证对象”。 我的项目的SecurityContext是: 我使用http://localhost:8060/oauth/token请求令牌?grant_type=password&client_i

  • 我正在编写一个程序,用于验证通过HTTP POST发送的用户名和密码,并根据ldap进行验证,无论验证是否成功,都将响应发送回用户。 我的Websecurity配置器实现 我的测试服务器.ldif

  • 我正在使用基于键斗篷头的身份验证来保护REST API。在那里,我想设置一个请求头字段(基本上是密钥斗篷授权头)之前,编写发生。 另一种解释是:如何获取javax。servlet。在密钥斗篷身份验证之前调用筛选器 在这次尝试中,我尝试调用被覆盖的resolve方法,并对。我可以注意到,它可以在身份验证发生之前调用。我还读取了一些请求头值,但没有setter将请求头设置到那里(to)。 在以这种或那