繁体   English   中英

使用Spring Security OAuth2验证不同的用户类型

[英]Authenticate different user types with Spring Security OAuth2

我想使用两种类型的用户:普通用户和管理员。 现在,我已经拥有一个基础结构,其中管理员和用户是两种完全不同的类型:用户拥有许多仅与它们相关的内容(控制器,表,服务等),与管理员相同。 因此,它们是数据库中的不同实体和不同表,并且我不想合并它们,因为它们是不同的。 但是现在只有用户可以使用Spring Security OAuth2登录,而管理员不能使用主体,因此他们无法登录。请注意,我使用自己的授权和资源服务器。

因此,我想允许Spring Security验证用户和管理员。 我还想为用户和管理员使用两个不同的登录端点以及两个不同的实体和表。

该怎么办?我应该怎么做?

UPD:

我想我应该为用户和管理员在oauth_client_details创建2个OAuth客户端, grant_typesoauth_client_details创建2个不同的grant_types ,并为用户和管理员创建2个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呢? 另外,也许我应该使用完全不同的方案?

<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

在新的1,创建OAuth2用户端oauth_client_detailscustom_grantauthorized_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添加到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.Create 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM