Spring Security Spring安全-新的访问令牌

qyzbxkaa  于 2023-02-04  发布在  Spring
关注(0)|答案(2)|浏览(157)

我有以下问题。
我有多租户系统(共享数据库和共享模式)。用户登录时生成的访问令牌包含tenantId的信息。想法是允许登录的用户更改tenat
例如:我的用户为3个租户(医院)工作。当他登录时,他应该可以更改医院。
因此,主要问题是如何为用户生成包含更新tenantId的新访问令牌。最好用户不必再次提供密码(因为他已经登录),并且他触发的对auth-server的请求将包含他的当前令牌(这将确认他当前已通过身份验证)和newTenandId。
下面是一些自定义代码:

@Service
public class CustomAuthenticationProvider implements AuthenticationProvider {
    
    @Autowired
    private MessageSource validationMessageSource;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
        
        SpringSecurityUserWithAdditionalData user = (SpringSecurityUserWithAdditionalData) userDetailsService.loadUserByUsername(username);
        
        return checkPassword(user, password);
    }
    
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
    
    private Authentication checkPassword(SpringSecurityUserWithAdditionalData user, String rawPassword) throws AuthenticationException {
        try {
            if (passwordEncoder.matches(rawPassword, user.getPassword())) {
                
                UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
                
                return token;
            } else {
                throw new GeneralException(validationMessageSource.getMessage("security.authentication.NotValid", new Object[] {}, LocaleContextHolder.getLocaleContext().getLocale()));
            }
        } catch (Exception e) {
            throw new BadCredentialsException(e.getMessage());
        }
        
    }
    
}
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration {
    
    @Autowired
    private CustomAuthenticationProvider authenticationProvider;
    
    // @formatter:off 
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
            ...
            .and()
                .logout()
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .logoutSuccessHandler(new CustomLogoutSuccessHandler())
            .and()
                .formLogin()
                .loginPage("/login")
                .loginPage("/changeTenant")
                .permitAll().and();
        return http.build();
    }
    // @formatter:on
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
    
    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }
    
    /**
     * JWT koji je generisao authorization server sadrzi granted permissions (Spring ih naziva granted authorities) u okviru "scope" claim-a. 
     * Umesto njega cemo koristiti custom claim koji sam nazvao GlobalConstants.JWT_CLAIM_ROLA_LIST za specifikaciju rola koje ima authenticated korisnik.
     * Spring koristi default instance JwtAuthenticationConverter koja ocekuje granted authorities u okviru "scope"/"scp" claim-a. 
     * Da bi koristili umesto standardno "scope" claim-a koristili claim GlobalConstants.JWT_CLAIM_ROLA_LIST override-ovan je JwtAuthenticationConverter.
     */
    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
        converter.setAuthoritiesClaimName(GlobalConstants.JWT_CLAIM_ROLA_LIST); // override authorities claim-a
        converter.setAuthorityPrefix(""); // eksplicitno definisemo nazive, bez podrazumevanih prefiksa (ROLE_ SCOPE_ i slicno)
        
        JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
        jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
        return jwtConverter;
    }
    
    @Bean
    InitializingBean forcePostProcessor(BeanPostProcessor meterRegistryPostProcessor, MeterRegistry registry) {
        return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, "");
    }
    
}

如果你还需要其他信息,请说。
我试着添加自定义字段到自定义登录表单,这将有隐藏的tenantId字段。但我不能设法使它工作。

webghufk

webghufk1#

身份验证过程应该设计为返回用户有权访问的所有承租者的列表,通常是权限列表。
另外,您需要一个后端调用(链接到UI),它允许用户从authn返回的权限列表中选择当前承租人。
当前租户的值必须存储在会话中。

at0kjp5o

at0kjp5o2#

如果你真的想通过auth角色来破解这个问题,你可以存储真实的auth令牌,然后只使用当前租户生成你自己的令牌。当用户更改租户时,他们会得到一个新的令牌(显然是在检查真实令牌之后)

相关问题