使用 RESTful 登录 API 验证我的 Spring Boot 应用程序

Posted

技术标签:

【中文标题】使用 RESTful 登录 API 验证我的 Spring Boot 应用程序【英文标题】:Authenticate my spring boot app using a restful login api 【发布时间】:2019-08-03 17:47:38 【问题描述】:

我正在开发一个 Spring Boot 应用程序,该应用程序根据端点登录 API 对用户进行身份验证,即:

我们通常直接检查数据库中保存的用户名和密码。但这次凭证是在另一个程序员开发的登录端点 API 中。

我的 Spring Boot 应用程序需要针对该登录 api 的用户身份验证“登录表单”。在授予对应用程序的访问权限之前。换句话说,用户名和密码来自未保存在数据库中的 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 我在发布之前搜索了堆栈溢出我找不到这类问题!!! 我认为堆栈溢出是不公平的。因为这里的人想学习我们没有经验,我们学习然后我们变得有经验 【参考方案1】:

首先你需要创建一个客户端来使用你的rest 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();
    


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

【讨论】:

答案看起来很有希望,我无法从网络上的任何地方得到它,谢谢。我将实施它并让你知道!唯一没有得到的部分是:“您需要在 Customer 对象中创建登录 api 响应的 DTO,并考虑如何将信息存储到 GrantedAuthority 列表中”? 您可以使用 tool 创建客户对象,您可以使用该对象从身份验证 api 序列化您的响应。 或者将整个json存入GrantedAuthority 可爱我会按照你刚刚告诉我的。谢谢尼古拉·赫里斯托夫 :) 我已经实现了你的指导,它可以正常工作。唯一的事情是登录页面返回无效的用户名/密码。我正在调试以找出原因,但如果您有任何想法,请告诉我。我很感激

以上是关于使用 RESTful 登录 API 验证我的 Spring Boot 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

Symfony 4 Restful API 使用 JWT 和 facebook 登录

如何使用 Spring Security 做一个 RESTful 登录 API?

Openid 和 RestFul API

带有 RESTful 身份验证的 Symfony2 应用程序,使用 FOSRestBundle 和 FOSUserBundle

laravel restful api 与 Auth

angularjs:restful api 的 oauth2 实现