我有以下问题。
我有多租户系统(共享数据库和共享模式)。用户登录时生成的访问令牌包含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字段。但我不能设法使它工作。
2条答案
按热度按时间webghufk1#
身份验证过程应该设计为返回用户有权访问的所有承租者的列表,通常是权限列表。
另外,您需要一个后端调用(链接到UI),它允许用户从authn返回的权限列表中选择当前承租人。
当前租户的值必须存储在会话中。
at0kjp5o2#
如果你真的想通过auth角色来破解这个问题,你可以存储真实的auth令牌,然后只使用当前租户生成你自己的令牌。当用户更改租户时,他们会得到一个新的令牌(显然是在检查真实令牌之后)