使用restful登录API认证我的Spring Boot应用程序

4

我正在开发一个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,
}

}


问题没问题。当用户会话处于活动状态时,您将使用响应信息吗? - Nikolay Hristov
是的,正确的。因为登录后我需要在我的应用程序中使用用户响应详细信息。 - Adam Obaidi
有什么建议吗?我在网上没有找到任何关于这种情况的信息!!! - Adam Obaidi
不幸的是,有些人唯一能做的就是贬低别人的问题。我不确定为什么我得到了-1分,因为在发布前我已经搜索过堆栈溢出了,而且我也没有找到这种类型的问题!!! - Adam Obaidi
我认为这不公平来自堆栈溢出。因为这里的人想要学习,我们没有经验,我们学习然后变得有经验。 - Adam Obaidi
1个回答

5

首先,您需要创建一个客户端来消费您的 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);
            }

        };

        }
}

你需要创建一个DTO来处理登录API响应中的Customer对象,并思考如何将信息存储到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();
    }

}

抱歉文章较长,如果有拼写错误我深感抱歉。正如我所说,这只是我的个人观点,还有许多其他解决方案。


嗨,我已经成功修复了登录问题,这对我来说是一段漫长的旅程!唯一剩下的问题是我需要从客户那里获取响应,以便在我的应用程序中使用,但现在我得到的是空的客户信息。有什么提示吗?谢谢。 - Adam Obaidi
这取决于您想在哪里以及如何访问用户信息。请查看编辑。 - Nikolay Hristov
感谢您的帮助,我在这种情况下提出了很多问题,因为我以前没有遇到过。无论如何,我已经实现了它,但仍然存在一个空客户。请查看我的Github:https://github.com/App-Teck/PermitsUI.git - Adam Obaidi
我尝试创建一个新的分支,但是失败了。所以我在我的存储库中进行了分叉(https://github.com/nikixp/PermitsUI)。问题出在你的Customer pojo中。 - Nikolay Hristov
非常感谢,我的应用程序已经完成。我从你这里学到了新的技能,希望所有的程序员都能分享他们的技能和知识,教育世界... - Adam Obaidi
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接