如何使用Spring Security进行多个身份验证配置

x33g5p2x  于2021-10-16 转载在 Spring  
字(7.2k)|赞(0)|评价(0)|浏览(1030)

在我们之前的文章中,我们分别使用 BASIC_AUTHJWT_AUTHLDAP_AUTH 配置文件实现了基本身份验证、JWT 身份验证、LDAP 身份验证。此外,我们已经看到如何使用 NO_AUTH``profile. 禁用 Spring Security

但是,有时我们的应用程序可能需要在单个配置文件中支持多个身份验证。例如,如果一个应用程序同时被内部用户和外部用户使用,那么内部用户可以使用 LDAP 身份验证,外部用户可以使用基于基本/JWT 令牌的身份验证。因此,我们将在 Spring Security 中创建一个 MULTI_AUTH 配置文件并配置多个身份验证提供程序。这样,用户就可以使用在请求的 Authorization 标头中传递的基本身份验证或 LDAP 身份验证或 JWT 令牌登录。

Spring Security使用多个身份验证配置

Profiles.java

为多重身份验证配置文件创建常量

package com.javachinna.config;

public class Profiles {

	private Profiles() {
	}

	public static final String NO_AUTH = "noauth";
	public static final String BASIC_AUTH = "basicauth";
	public static final String JWT_AUTH = "jwtauth";
	public static final String LDAP_AUTH = "ldapauth";
	public static final String MULTI_AUTH = "multiauth";
}
MultiAuthSecurityConfig.java

@Profile(Profiles.MULTI_AUTH) 注释仅用于在应用程序使用“multiauth”配置文件运行时启用多个身份验证提供程序。

MultiAuthSecurityConfig 类扩展了 WebSecurityConfigurerAdapter 以使用多个身份验证提供程序配置 Spring Security。

当定义了多个身份验证提供程序时,将按照它们声明的顺序查询这些提供程序。因此,如果一个身份验证失败,那么它将转移到下一个身份验证提供程序。

package com.javachinna.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.LdapShaPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.javachinna.model.Role;

import lombok.RequiredArgsConstructor;

@Profile(Profiles.MULTI_AUTH)
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class MultiAuthSecurityConfig extends WebSecurityConfigurerAdapter {

	private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
	private final UserDetailsService jwtUserDetailsService;
	private final JwtRequestFilter jwtRequestFilter;
	private final LdapUserAuthoritiesPopulator ldapUserAuthoritiesPopulator;

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		// Returns LdapAuthenticationProviderConfigurer to allow customization of the
		// LDAP authentication
		auth.ldapAuthentication()
				// Pass the LDAP patterns for finding the username.
				// The key "{0}" will be substituted with the username
				.userDnPatterns("uid={0},ou=users")
				// Pass search base as argument for group membership searches.
				.groupSearchBase("ou=groups")
				// Configures base LDAP path context source
				.contextSource().url("ldap://localhost:10389/dc=javachinna,dc=com")
				// DN of the user who will bind to the LDAP server to perform the search
				.managerDn("uid=admin,ou=system")
				// Password of the user who will bind to the LDAP server to perform the search
				.managerPassword("secret").and()
				// Configures LDAP compare operation of the user password to authenticate
				.passwordCompare().passwordEncoder(new LdapShaPasswordEncoder())
				// Specifies the attribute in the directory which contains the user password.
				// Defaults to "userPassword".
				.passwordAttribute("userPassword").and()
				// Populates the user roles by LDAP user name from database
				.ldapAuthoritiesPopulator(ldapUserAuthoritiesPopulator);
		// Basic / JWT authentication
		auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
	}

	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
		// Disable CSRF
		httpSecurity.csrf().disable()
				// Only admin can perform HTTP delete operation
				.authorizeRequests().antMatchers(HttpMethod.DELETE).hasRole(Role.ADMIN)
				// any authenticated user can perform all other operations
				.antMatchers("/products/**").hasAnyRole(Role.ADMIN, Role.USER).and().httpBasic()
				// Permit all other request without authentication
				.and().authorizeRequests().anyRequest().permitAll()
				// Reject every unauthenticated request and send error code 401.
				.and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
				// We don't need sessions to be created.
				.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

		// Add a filter to validate the tokens with every request
		httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
	}
	
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}
认证顺序
  1. JwtRequestFilter 会在请求头中寻找令牌。

  2. 如果找到令牌,那么它将验证令牌并在 Spring Security Context 中手动设置身份验证。

  3. 否则,记录警告并继续进行下一次身份验证

  4. LDAP 身份验证提供程序将使用 LDAP 服务器验证用户凭据。

  5. 如果认证成功,那么它会尝试从数据库中通过用户名获取用户权限。如果找到权限,则将其分配给用户。否则,将分配默认用户角色并完成身份验证过程。

  6. 否则,它将继续进行下一个身份验证。

  7. 基本身份验证提供程序将使用 UserDetailsService 实现通过数据库验证用户凭据。

  8. 如果认证成功,则认证主体将在安全上下文中设置为已配置的权限,过程完成。

  9. 否则,身份验证失败,过程完成。

LdapUserAuthoritiesPopulator.java

如果在数据库中找不到给定 LDAP 用户名的用户权限,则修改此类以分配默认用户角色。

package com.javachinna.config;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.stereotype.Component;

import com.javachinna.model.Role;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
@Component
public class LdapUserAuthoritiesPopulator implements LdapAuthoritiesPopulator {

	private final UserDetailsService userDetailsService;

	@Override
	public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
		Collection<? extends GrantedAuthority> authorities = new HashSet<>();
		try {
			authorities = userDetailsService.loadUserByUsername(username).getAuthorities();
		} catch (Exception e) {
			log.warn("Unable to fetch the user authorities from the database. Hence, assigning default user role");
			authorities = Arrays.asList(new SimpleGrantedAuthority(Role.ROLE_USER));
		}
		return authorities;
	}
}

添加 MULTI_AUTH 配置文件

我们需要将 MULTI_AUTH 配置文件添加到以下 JWT 过滤器和控制器中,以便为该配置文件启用它们。

JwtRequestFilter.java
@Component
@Profile({Profiles.JWT_AUTH, Profiles.MULTI_AUTH})
@RequiredArgsConstructor
public class JwtRequestFilter extends OncePerRequestFilter {
JwtAuthenticationController.java
@Profile({Profiles.JWT_AUTH, Profiles.MULTI_AUTH})
@RestController
@CrossOrigin
@RequiredArgsConstructor
public class JwtAuthenticationController {

使用多重身份验证配置文件运行

您可以使用 mvn spring-boot:run -Dspring-boot.run.profiles=multiauth 运行该应用程序,然后在浏览器中点击 URL http://localhost:8080/swagger-ui.html。您应该能够使用上述任何身份验证来执行服务。

源代码

和往常一样,你可以从下面的 Github 获取源代码

https://github.com/JavaChinna/spring-boot-rest-multi-auth

结论

这就是所有的人!在本文中,您学习了如何在单个配置文件中为 Spring Boot RESTful 服务配置多个身份验证提供程序。

相关文章