我们目前正在使用Spring Boot(1.2.6)编写一个中间件,以向我们的移动/ Web应用程序公开REST API . 中间件没有数据库,并且得到了我们客户的一些远程服务的支持 .

对于Login,我们将用户名/密码和一些参数(ip,用户代理等)发送到远程服务,并获取有关用户(名称,上次登录,布尔更改密码标志等)的一些信息,包括会话ID . 我们写了一些bean,我们在相应的控制器中使用它们:

@RestController
@RequestMapping(value = "/user", produces = "application/json")
public final class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public LoginResponse login(@RequestBody final LoginRequest request, final HttpServletRequest servletRequest) {
        final LoginResponse response = new LoginResponse();
        final LoginServiceRequest serviceRequest = new LoginServiceRequest();

        serviceRequest.setAdditionalRequestData(AdditionalRequestData.getInstance(servletRequest));
        serviceRequest.setUsername(request.getUsername());
        serviceRequest.setPassword(request.getPassword());

        final LoginData serviceResponse = userService.login(serviceRequest);

        response.setChangePassword(serviceResponse.isChangePassword());
        // setting other params here...

        return response;
    }
}

据我所知,Spring Security通常依赖于在控制器之前工作的servlet过滤器 . 例如,如果我在配置中启用formLogin,它会启用 UsernamePasswordAuthenticationFilter ,它根据我定义的 AuthenticationManager bean处理身份验证 . 但是,在这种情况下我需要身份验证响应,并且我们发送以JSON编码的请求参数 . 所以过滤器似乎不适合我们 .

相反,我创建了一个 AuthenticationProviderAuthenticationToken 并将上面的代码改为这样的代码:

@RestController
@RequestMapping(value = "/user", produces = "application/json")
public final class UserController {
    @Autowired
    private AuthenticationManager auth;

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public LoginResponse login(@RequestBody final LoginRequest request,
        final HttpServletRequest servletRequest) throws ServletException {

        final LoginResponse response = new LoginResponse();
        final Authentication authenticationToken = new CustomAuthenticationToken(
            request.getUserId(),
            request.getPassword(),
            AdditionalRequestData.getInstance(servletRequest)
        );

        final LoginData loginData = 
            ((CustomAuthenticationToken) auth.authenticate(authenticationToken)).getLoginData();

        response.setChangePassword(loginData.isChangePassword());
        // setting other params here...

        return response;
    }
}

AuthenticationProvider 负责调用 userService.login 方法以及将 AuthenticationToken 设置为 SecurityContext .

这是我们的安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
            .authorizeRequests()
                .antMatchers("/version/**").permitAll()
                .anyRequest().hasAnyRole(Constants.ROLE_USER);
        // @formatter:on
    }
}

这种manuel方法确实有效 . 我们还利用权限(ROLE_USER等..)来授予对不同 endpoints 的访问权限 .

有更好的解决方案吗?当我们这样做时,您认为我们失去了Spring Security的一些功能吗?