使用宁静的登录API对我的Spring Boot应用进行身份验证

时间:2019-03-13 05:56:31

标签: spring rest api spring-boot spring-security

首先,大家好不要低估我的问题,因为我到处搜索过,但是找不到清晰的实现方式!但是仍然有些人不在乎并继续低估我的问题...

我正在开发一个Spring Boot应用,该应用根据端点登录API对用户进行身份验证,即: 我们通常会直接检查保存在数据库中的用户名和密码。但是这次的凭证是 在另一个程序员开发的Login端点API中。

我的Spring启动应用程序,需要针对该登录api的用户身份验证“登录表单”。在授予对应用程序的访问权限之前。换句话说,用户名和密码来自未保存在DB中的API!其他人已经开发的登录API。任何想法?我以前没有做过!登录API为:

POST: domain/authenticate/user

身体是:

{    "username" : "user",  
     "password" : "test"
}

响应为:

{
"customer": {
    "id": 62948,
    "email": "test@test.com.au",
    "givenName": "A",
    "familyName": "OB",
    "user": {
        "id": 63158,
        "version": 1,
        "email": "adamo@test.com.au",
        "password": "113b984921197350abfedb0a30f6e215beeda6c718e36559f48eae946664b405c77bc6bab222fe8d3191f82925921438b6566dda76613aa6cd4416e5d9ae51c8",
        "givenName": "A",
        "familyName": "OB",
     },
    "vehicles": [
        {
            "id": 79369,
            "version": 0,
            "country": "Australia"
            },
            "newState": null,
        }
    ],
    "fundingSources": [
        {
            "@class": "au.com.test.test",
            "id": 54795,
            "version": 0,
        }
    ],

    "citySuburb": null,
}

}

1 个答案:

答案 0 :(得分:2)

首先,您需要创建一个客户端以使用剩余的api进行身份验证:

@Service
public class AuthService {

    @Bean
    public RestTemplate authRestTemplate() {
        return new RestTemplateBuilder().rootUri("http://domain/authenticate").build();
    }

    public Customer authenticate(MultiValueMap<String, String> request) {
        return authRestTemplate().postForObject("/user", request, Customer.class);
    }

    public MultiValueMap<String, String> createRequest(String username, String password) {
        MultiValueMap<String, String> request = new LinkedMultiValueMap<>();
        request.add("username", username);
        request.add("password", password);
        return request;
    }

}

然后,您必须创建一个组件或服务来使用该客户端:

@Service
public class AuthenticationService implements AuthenticationProvider {

private AuthService authService;

@Autowired
public void setAuthService(AuthService authService) {
    this.authService = authService;
}

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

    String username = authentication.getName();
    String password = authentication.getCredentials().toString();

    Customer customer = authService.authenticate(authService.createRequest(username, password));
    if (customer != null) {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
//here you need to store your Customer object to use it anywhere while the user is logged in
// take a look on the edit
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new UsernamePasswordAuthenticationToken(username, password, grantedAuthorities);
    }
    throw new AuthenticationServiceException("Invalid credentials.");

}

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

}

最后,您需要使用自定义身份验证服务进行基本安全性配置:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration implements WebMvcConfigurer {

    private AuthenticationService authenticationService;

    @Autowired
    public void setAuthenticationService(AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }

    @Bean
    public WebSecurityConfigurerAdapter webSecurityConfig() {
        return new WebSecurityConfigurerAdapter() {
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http
                        .csrf()
                        .disable()
                        .authorizeRequests()
                        .antMatchers("/webjars/**").permitAll()
                        .anyRequest().authenticated()
                        .and()
                        .formLogin()
                        .loginPage("/login")
                        .permitAll()
                        .and()
                        .logout()
                        .permitAll();
            }

            @Override
            protected void configure(AuthenticationManagerBuilder builder) throws Exception {
                builder.authenticationProvider(authenticationService);
            }

        };

        }
}

您需要在Customer对象中创建登录api响应的DTO,并考虑如何将信息存储到GrantedAuthority的列表中

您可以使用许多其他选项,但这对我来说很容易。

编辑:这只是一个如何为您的身份验证api实现GrantedAuthority的想法:

首先,您需要一个实现接口并存储整个json的对象:

public class CustomerGrantedAuthority implements org.springframework.security.core.GrantedAuthority {

    private String customerJson;

    public CustomerGrantedAuthority(String customerJson){
        this.customerJson = customerJson;
    }

    @Override
    public String getAuthority() {
        return customerJson;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CustomerGrantedAuthority that = (CustomerGrantedAuthority) o;
        return java.util.Objects.equals(customerJson, that.customerJson);
    }

    @Override
    public int hashCode() {
        return java.util.Objects.hash(customerJson);
    }

    @Override
    public String toString() {
        return this.customerJson;
    }

}

更好的解决方案是制造一个对象并将其作为对象存储,而不是作为字符串存储,而仅作为示例,它是一个字符串。

然后,您需要在访问身份验证api的代码中更改AuthenticationService

String customer = new RestTemplate().postForObject("http://domain/authenticate/user", createRequest(username, password), String.class);
    if (customer != null) {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
    grantedAuthorities.add(new CustomerGrantedAuthority(new ObjectMapper().writeValueAsString(customer)));
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new UsernamePasswordAuthenticationToken(username, password, grantedAuthorities);
    }
    throw new AuthenticationServiceException("Invalid credentials.");

public MultiValueMap<String, String> createRequest(String username, String password) {
            MultiValueMap<String, String> request = new LinkedMultiValueMap<>();
            request.add("username", username);
            request.add("password", password);
            return request;
        }

这取决于您希望在何处以及如何访问应用程序中的用户信息,但只是要查看其是否有效,您可以使用简单的RestController进行测试,该测试在用户登录时应该可见:

@RestController
public class TestController {

    @GetMapping(value = "/auth")
    public ResponseEntity getAuth() {
        Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        CustomerGrantedAuthority customer = (CustomerGrantedAuthority) authorities.stream().findFirst().orElse(null);
        return customer != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(customer.getAuthority()) : ResponseEntity.notFound().build();
    }

}

很长的帖子,很抱歉,如果有拼写错误,我深表歉意。正如我所说的,这只是我的观点,还有许多其他解决方案。

相关问题