我想使用两种类型的用户:普通用户和管理员。现在我已经有了一个基础架构,其中管理员和用户是两种完全不同的类型:用户有许多只与他们相关的东西(控制器、表、服务等),管理员也是如此。因此,它们在数据库中是不同的实体和不同的表,我不想组合它们,因为它们是不同的。但现在只有用户可以使用Spring Security OAuth2登录,但管理员不是主体,他们不能登录。请注意,我使用自己的授权和资源服务器。
因此,我希望允许Spring Security对用户和管理员进行身份验证。我还想为用户和管理员使用两个不同的登录端点和两个不同的实体和表。
如何才能做到这一点,或者我应该怎么做?
更新:
我想我应该创建两个OAuth客户端,在oauth_client_details
中使用两个不同的grant_types
,并为用户和管理员创建两个AbstractTokenGranters
。
我已经有一个针对用户的自定义AbstractTokenGranter
,它像这样对用户进行身份验证:
//getOAuth2Authentication()
User user = userService.getUserByPhone(username);
if(user == null)
throw new BadCredentialsException("Bad credentials");
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(Long.toString(user.getId()), password)
);
//I use Long.toString(user.getId()) because some users use FB instead of the phone,
//so I have one more `AbstractTokenGranter` for social networks,
//I don't mention about it in this post, so don't be confused
据我所知,AuthenticationManager
调用UserDetailsService
,它现在看起来像这样:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = userRepository.findById(Long.parseLong(username)).orElseThrow(
() -> new UsernameNotFoundException("User not found with id : " + id)
);
return user;
}
但是如果我为管理员再创建一个AbstractTokenGranter
,那么当前的UserDetailsService
将不知道它收到了谁的id -管理员id或用户id。
作为一种解决方案,我认为我需要为管理员再创建一个UserDetailsService
。但是如何使用多个UserDetailsService
呢?另外,也许我应该使用一个完全不同的方案?
发布于 2019-08-09 09:24:37
1.在oauth_client_details
表中创建新的OAuth2客户机,其中custom_grant
在authorized_grant_types
中。
2.创建:
public class CustomTokenGranter extends AbstractTokenGranter {
//...
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> params = tokenRequest.getRequestParameters();
String username = params.getOrDefault("username", null);
String password = params.getOrDefault("password", null);
if(username == null || password == null)
throw new BadCredentialsException("Bad credentials");
CustomAuthenticationToken token = new CustomAuthenticationToken(username, password);
Authentication authentication = authenticationManager.authenticate(token);
}
}
3.在AuthorizationServerConfigurerAdapter
中添加此授权者
private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
List<TokenGranter> granters = new ArrayList<TokenGranter>(Arrays.asList(endpoints.getTokenGranter()));
granters.add(new CustomGrantTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), "custom_grant"));
return new CompositeTokenGranter(granters);
}
现在,CustomGrantTokenGranter
将收到所有授权类型为custom_grant
的授权请求。
4.创建CustomAuthenticationToken extends UsernamePasswordAuthenticationToken
5.创建:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private PasswordEncoder adminPasswordEncoder;
@Autowired
private UserDetailsService adminDetailsService;
@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
String username = auth.getName();
String password = auth.getCredentials().toString();
UserDetails adminDetails = adminDetailsService.loadUserByUsername(username);
//adminDetailsService.loadUserByUsername(username) returns Admin inside UserDetails
if (adminPasswordEncoder.matches(password, adminDetails.getPassword()))
return new UsernamePasswordAuthenticationToken(adminDetails, password, adminDetails.getAuthorities());
else
throw new BadCredentialsException("Bad credentials");
}
@Override
public boolean supports(Class<?> auth) {
return auth.equals(CustomAuthenticationToken.class);
}
}
在这里,您可以使用与其他提供程序不同的UserDetailsService
6.在WebSecurityConfigurerAdapter
中添加CustomAuthenticationProvider
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider);
}
//...
}
摘要:使用此方案,您可以使用任意数量的用户类型。如果为Admin implements UserDetails
,那么您可以很容易地在服务器上将其用作Principal
。
发布于 2019-08-08 03:14:10
<security:http pattern="/oauth/token" use-expressions="true" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<security:intercept-url pattern="/**" method="GET" access="ROLE_DENY"/>
<security:intercept-url pattern="/**" method="PUT" access="ROLE_DENY"/>
<security:intercept-url pattern="/**" method="DELETE" access="ROLE_DENY"/>
<security:intercept-url pattern="/oauth/token" access="permitAll"/>
<security:anonymous enabled="false"/>
<security:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<!-- include this only if you need to authenticate clients via request
parameters -->
<security:custom-filter ref="contentTypeFilter" before="BASIC_AUTH_FILTER"/>
<security:custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER"/>
<security:access-denied-handler ref="oauthAccessDeniedHandler"/>
<security:csrf disabled="true"/>
</security:http>
您可以定义自定义clientDetailService并覆盖loadUserByUserName方法。您可以自行决定是否可以查询不同的表和权限,也可以更改结构。这就是我可以说的,不需要更多的描述。
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ClientDetails clientDetails;
try {
clientDetails = clientDetailsService.loadClientByClientId(username);
} catch (NoSuchClientException e) {
throw new UsernameNotFoundException(e.getMessage(), e);
}
String clientSecret = clientDetails.getClientSecret();
if (clientSecret== null || clientSecret.trim().length()==0) {
clientSecret = emptyPassword;
}
return new User(username, clientSecret, clientDetails.getAuthorities());
}
可以修改此部件以更改authentication-manager-ref="clientAuthenticationManager“的结构:>
如果您使用的不是基于xml的,则可以检查基于注释的链接:https://www.baeldung.com/spring-security-authentication-with-a-database
https://stackoverflow.com/questions/57400607
复制相似问题