Authentication Manager Builder in Spring Security



我正在探索spring安全性,并试图构建一个小应用程序,其中我有一个实体名称User和一个userRepository,具有一个声明的方法findByUserName(String userName)

@Entity
@Table(name="user")
class User {
@id
private Long id;

private String userName;
private String password;
}

我听说spring安全取决于原则而不是用户。所以我们必须有一个类来实现UserDetails(由spring security提供)。

这背后的原因是什么?

其次,一旦我们写完所有这些代码,我们需要把它配置成一个类,我已经这样做了,如下所示:
public class AppSecurityConfid extends WebSecurityCongigurerAdapter {
// here we have to autowire the service class which we have made to call the 
userRepository and find the user based on userName
@Bean
public DAOAuthenicationProvider authenicationProvider() {
// wherein we create an instance and pass the autowired instance and set the 
password encoder and return the instance
}
protected void configurer(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
} 

事情到这里是有意义的,但为什么我们需要认证构建管理器在这个方案的东西?

我不是专家,但我想说些什么,也许能有所帮助:

Spring使用"在后台"一种在激活Spring Security时检索用户数据进行身份验证的方法。当然,这个方法可以被重写,这样开发人员就可以改变Spring获取这些数据的方式,以支持数据在不同的表、文件、API REST查询等中被分隔的情况。

身份验证数据结构为一个列表,其中每个元素对应于用于验证每个用户的数据。该数据结构为包含3个元素的元组:String username,String hashedPasswordboolean isAccountActive

您需要提供一种方法来获取每个用户的数据。您不需要提供数据的显式,只是获取它的方式(方法)。一种方法(如您所说)是创建一个实现UserDetailsService的类,例如,它强制您实现UserDetails loadUserByUsername(String email);。在这个方法中,您需要提供一个实现UserDetails的类的实例,它对应于用户的UserDetails,用户名作为参数传递。Spring Security在后台使用这种方法(以及类似的方法)。当某个用户试图访问您的web服务器时,检索其UserDetails。

如果Userdetails与请求中提供的凭据匹配,Spring将允许请求击中你的控制器;否则它将抛出HTTP 401。当然还有其他的身份验证方法,但为了简单起见,我们将凭据理解为HTTP基本身份验证的用户/密码。

那么,为什么我们需要一个实现UserDetails的类呢?因为在Spring Security中,如果类必须用于内部身份验证,则该类需要履行的契约。还可以将业务逻辑与安全逻辑从User类中分离出来。也许创建自己的类来扩展UserDetails是最好的主意,但实际上并不是必需的。例如,如果你有自己的类来包含用户的数据,你只需要了解如何将你的User实例转换为UserDetails,这样Spring Security就可以透明地使用它,相反:如何将UserDetails实例转换为你的一个用户。

例如,这是一个方法来获得User实例使用UserDetails实例,目前在Spring Boot中被认证。

@Service
public class SecurityServiceClass{
@Override
public User getLoggedUser() {
String username = ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
Optional<User> user = this.userService.get().stream().filter(r -> r.getEmail().equals(username)).findFirst();
UserDetails userDetails = ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal());
// TODO: make error in case of null
return user.orElse(new User());
}
}

这里,我通过从UserDetails中检索用户名并将其查询到DB以恢复User来检索User。我正在使用存储库类访问DB。

这里我做了相反的事情,通过基于User的相关数据创建Userdetails实例,将User转换为UserDetails。(注意,我使用电子邮件作为用户名)

@Service
public class UserServiceClass extends GenericServiceClass<User, UUID> {
@Autowired
public UserServiceClass(UserRepository userRepository) {
super(userRepository);
}

@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Optional<User> selected = ((UserRepository) this.genericRepository).getUserByEmail(s);
if (selected.isPresent())
{
// Obtain user by email (username)
User user = selected.get();
// Obtain the roles of this user to construct the instance of UserDetails for SpringBoot Security.
Set<Role> roles = user.getRoles();
return org.springframework.security.core.userdetails.User
.withUsername(s)
.roles(roles.stream().toArray(
(n) -> {
return new String[n];
}
))
.password(user.getHashedPassword())
.build();
}
else
{
throw new UsernameNotFoundException("The user with email " + s + " is not registered in the database");
}
}

最后,关于AuthenticationManagerBuilder:这是一个用于配置身份验证的方法。据我所知,您可以定义应用程序应该如何获取UserDetails。我不确定您是否可以提供一个方法或lambda来检索认证String username,String hashedPasswordboolean isAccountActive的三元组。我所知道并在我的应用程序中所做的是提供用于从数据库检索三元组的SQL查询,因为我有那里的所有信息。代码:

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationBuilder) throws Exception
{
Session session = this.sessionFactory.getCurrentSession();
session.beginTransaction();
authenticationBuilder.userDetailsService(this.userDetailsService()).passwordEncoder(this.passwordEncoder()).and()
.jdbcAuthentication().dataSource(this.dataSource)
.usersByUsernameQuery("select email, hashed_password as passw, true from user where email = ?")
.authoritiesByUsernameQuery("SELECT user.email, CONCAT(elementpermission.journal_id, '_', elementpermission.authority)n" +
"FROM user, elementpermissionn" +
"WHERE elementpermission.user = user.uuid n" +
"AND user.email = ?");

session.getTransaction().commit();
session.close();
}

TL;博士Spring Security需要实现接口UserDetails契约的实例,因为Spring Security使用UserDetails接口来获取用于身份验证的相关数据。

认证管理器构建器用于配置如何获取用于认证的数据。

如果你想了解更多信息,可以查看以下链接:

  • https://www.baeldung.com/spring-security-jdbc-authentication
  • https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/jdbc.html
  • jdbcAuthentication()代替inMemoryAuthentication()不能't给访问- Spring安全和Spring Data JPA

最新更新