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

Spring Security-使用自定义DB授权与Auth0预验证

董高逸
2023-03-14

我目前正在使用Spring Security开发Spring Boot REST应用程序。我的工作场所使用Auth0(提供用户管理的外部第三方服务)进行身份验证,并已要求我在此应用程序中实现它。身份验证发生在用React编写的前端应用程序中。前端应用程序显示登录表单,并将用户名和密码发送给Auth0,Auth0验证凭据,并在验证用户时返回JWT令牌。

在此之后,前端应用程序将通过Authorize头中的JWT令牌从我的应用程序调用REST服务。Spring Security使用Auth0插件验证该令牌,并允许执行请求。我已经测试了这么多,以达到预期效果。代码如下:

import java.util.Arrays;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import com.auth0.spring.security.api.JwtWebSecurityConfigurer;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
    
    @Value(value = "${auth0.apiAudience}")
    private String apiAudience;
    @Value(value = "${auth0.issuer}")
    private String issuer;
    
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        configuration.setAllowCredentials(true);
        configuration.addAllowedHeader("Authorization");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors();
        JwtWebSecurityConfigurer  //Auth0 provided class performs per-authentication using JWT token
            .forRS256(apiAudience, issuer)
            .configure(http)
            .authorizeRequests()
            .antMatchers(HttpMethod.GET, "/Test/public").permitAll()
            .antMatchers(HttpMethod.GET, "/Test/authenticated").authenticated();
    }
    
}

现在,一旦完成了身份验证,我发现安全上下文中的主体将使用Auth0中的用户id进行更新。我已经通过以下代码片段验证了这一点:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String name = authentication.getName(); // Returns the Auth0 user id.

我希望做的下一步是使用此用户id将用户与我现有数据库模式中的角色和权限匹配。因此,我需要实现一个自定义授权机制,该机制也可以插入到Spring Security中。换句话说,用户的角色必须在(预)身份验证完成后不久加载到安全上下文中。我该如何实现这一点?是否有一些类需要扩展或实现一些接口?

共有2个答案

马安邦
2023-03-14

好吧,我找到了一个解决方案,虽然我觉得有点脏。按照官方Auth0类的奇怪结构,我所做的可能被描述为黑客行为。不管怎样,下面是:

首先,我通过实现AuthenticationUserDetailsService接口创建了一个自定义用户详细信息服务:

@Service
public class VUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationJsonWebToken> {

@Autowired
UserRepository userRepository;

Logger logger = LoggerFactory.getLogger(VUserDetailsService.class);

@Override
@Transactional(readOnly = true)
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationJsonWebToken token) throws UsernameNotFoundException {
    logger.debug("User id: "+token.getName());
    // Verify whether there is an entry for this id in the database.
    User user = userRepository.findByAuxillaryId(token.getName());
    
    if(user == null)
        throw new UsernameNotFoundException("The user with id "+token.getName()+" not found in database.");
    
    logger.debug("Obtained user details from db: "+user.toString());
    List<GrantedAuthority> authoritiesList = new ArrayList<>();
    // Get user roles
    List<UserRole> userRoles = user.getUserRoles();
    if(userRoles != null) logger.debug("Number of user roles:"+userRoles.size());
    
    for(UserRole userRole : userRoles) {
        logger.debug(userRole.getCompositeKey().getRole());
        authoritiesList.add(new SimpleGrantedAuthority(userRole.getCompositeKey().getRole()));
    }
        
    return new org.springframework.security.core.userdetails.User(token.getName(), "TEMP", authoritiesList);
}
}

在这里,辅助id是在Auth0中创建用户时分配的用户id。请注意,预验证验证JsonWebToken也是Auth0提供的类。

在这之后,我创建了一个自定义身份验证提供程序,扩展了Auth0提供的Jwt验证提供程序

public class VAuthenticationProvider extends JwtAuthenticationProvider {
    
    public VAuthenticationProvider(JwkProvider jwkProvider, String issuer, String audience) {
        super(jwkProvider, issuer, audience);
    }

    @Autowired
    VUserDetailsService vUserDetailsService;
    
    Logger logger = LoggerFactory.getLogger(VAuthenticationProvider.class);

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        logger.debug("*** Processing authentication for token: "+authentication.getName());
        logger.debug("*** Current granted authorities: "+authentication.getAuthorities());
        UserDetails userDetails = vUserDetailsService.loadUserDetails((PreAuthenticatedAuthenticationJsonWebToken) authentication);
        authentication = new PreAuthenticatedAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
        return authentication;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        //com.auth0.spring.security.api.authentication.PreAuthenticatedAuthenticationJsonWebToken
        return authentication.equals(PreAuthenticatedAuthenticationJsonWebToken.class);
    }
}

然后我在我的安全配置类中使用了这个身份验证提供程序:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value(value = "${auth0.apiAudience}")
    private String apiAudience;
    @Value(value = "${auth0.issuer}")
    private String issuer;

    @Autowired
    VUserDetailsService vUserDetailsService;

    Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
    
    @Bean
    public VAuthenticationProvider authProvider() {
        JwkProvider jwkProvider = new JwkProviderBuilder(issuer).build(); //Auth0 provided class
        VAuthenticationProvider vAuthProvider = new VAuthenticationProvider(jwkProvider, issuer, apiAudience);
        return vAuthProvider;
        
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors();
        JwtWebSecurityConfigurer.forRS256(apiAudience, issuer, authProvider())
                .configure(http)
                .authorizeRequests().antMatchers(HttpMethod.GET, "/Test/public").permitAll()
                .antMatchers(HttpMethod.GET, "/Test/authenticated").authenticated()
                .antMatchers(HttpMethod.GET, "/admin/*").hasRole("ADMIN") //Not Auth0 role, defined in my DB.
                .antMatchers(HttpMethod.GET, "/Test/root").hasRole("ROOT"); //Not Auth0 role, defined in my DB.
    }
/* Code ommitted */

现在,我的所有请求都会根据数据库中的角色进行筛选。因此,Auth0仅用于身份验证,授权基于数据库中的角色。

如果有人认为这个解决方案可以改进,请告诉我。

刘畅
2023-03-14

我想你要找的是AuthenticationProvider接口。以下是我如何处理身份验证的两个示例:

DAO身份验证

@Component
public class DaoAdminAuthenticationProvider extends DaoAuthenticationProvider {
private static final Logger LOG = 
LoggerFactory.getLogger(DaoAdminAuthenticationProvider.class);

private final AdminUserRepository adminUserRepository;

public DaoAdminAuthenticationProvider(AdminUserRepository adminUserRepository, DaoAdminUserDetailsService daoAdminUserDetailsService) {
    this.adminUserRepository = adminUserRepository;
    setPasswordEncoder(new BCryptPasswordEncoder(11));
    this.setUserDetailsService(daoAdminUserDetailsService);
}

@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {

    AdminUser adminUser = adminUserRepository.findByEmail(auth.getName());
    if (adminUser == null) {
        LOG.info("Invalid username or password");
        throw new BadCredentialsException("Invalid username or password");
    }

    Authentication result = super.authenticate(auth);
    return new UsernamePasswordAuthenticationToken(adminUser, result.getCredentials(), result.getAuthorities());
}

@Override
public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}

JwtAuthenticationProvider

@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOG = 
LoggerFactory.getLogger(JwtAuthenticationProvider.class);

private static final String EX_TOKEN_INVALID = "jwt.token.invalid";

private final JwtTokenService jwtTokenService;

@SuppressWarnings("unused")
public JwtAuthenticationProvider() {
    this(null);
}

@Autowired
public JwtAuthenticationProvider(JwtTokenService jwtTokenService) {
    this.jwtTokenService = jwtTokenService;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    try {
        String token = (String) authentication.getCredentials();
        String username = jwtTokenService.getUsernameFromToken(token);

        return jwtTokenService.validateToken(token)
                .map(aBoolean -> new JwtAuthenticatedProfile(username))
                .orElseThrow(() -> new TokenException(EX_TOKEN_INVALID));

    } catch (JwtException ex) {
        LOG.error("Invalid JWT Token");
        throw new TokenException(EX_TOKEN_INVALID);
    }
}

@Override
public boolean supports(Class<?> authentication) {
    return JwtAuthentication.class.equals(authentication);
}
}

其他类,如JwtTokenService等,我也实现了。但是关于你的问题,我认为答案是使用AuthenticationProvider接口。

 类似资料:
  • 技术栈在前端是reactJS,后端由APIGateway和Lambda供电。我正在使用Auth0作为我的react应用程序中的身份服务。通过Auth0提供的一个社交登录进行身份验证时,我会返回、和。此外,我可以使用获取用户信息,例如。现在,我需要保护/阻止对我的后端即APIGateway和lambda的访问。 有很多选项来保护对AWS APIGateway的访问,如IAM授权器、自定义授权器。有一

  • 我有自定义的身份验证筛选器,它创建PreAuthenticatedAuthenticationToken并将其存储在安全上下文中。过滤器工作良好,它创建具有相应授权权限的令牌“role_user”和“role_adminuser”。这是我的配置: 我将在SecurityContextPersistenceFilter之前插入过滤器,如:Spring security and custom Auth

  • 我们使用Spring Security OAuth2保护我们的REST服务(用于服务器到服务器通信,不涉及用户)。但是,当您尝试访问浏览器中的受保护资源时,它将显示: 我们希望这是我们自己选择的自定义页面。有办法吗? 设置“拒绝访问”页面不起作用。首先,它需要定义一个登录页面,我们没有,因为这是一个纯服务器到服务器的通信。另一个原因是,这个属性自Spring 3.0..或类似的版本以来就被弃用了。

  • 问题内容: 这是我的情况: 一个Web应用程序对许多应用程序执行某种SSO 登录的用户,而不是单击链接,该应用就会向正确的应用发布包含用户信息(名称,pwd [无用],角色)的帖子 我正在其中一个应用程序上实现SpringSecurity以从其功能中受益(会话中的权限,其类提供的方法等) 因此,我需要开发一个 自定义过滤器 -我猜想-能够从请求中检索用户信息,通过自定义 DetailsUserSe

  • 我正在寻找如何使用以下设置测试Spring Boot REST API的方法: _ _ 我的测试用例: _ 不幸的是,我在测试中得到以下响应: 在请求之后,我还调试了不同的spring过滤器,其中SecurityContext身份验证为null,随后切换为AnonymousAuthenticationToken 我不知道为什么SecurityContext在请求后为null,而不是用@WithCu

  • 功能说明 目睹直播提供了一系列的授权观看方式,如密码验证,付费观看等,然而由于客户业务的多样性,实现如:接入客户自身账户体系,登陆OA系统后再跳转到目睹观看直播等一系列更高级的授权观看方式,此时就需要使用自定义授权。 自定义授权逻辑图 功能设置 首先,需在 某个频道 - 频道管理 - 授权观看 - 授权类型 中点击自定义授权,并输入您希望在观众进入观看页时跳转的链接,如: http://your-