Spring Boot с сеансом только для чтения для единого входа - PullRequest
0 голосов
/ 07 апреля 2020

У нас есть старое приложение Spring (A) (которое не использует spring-boot), которое обрабатывает аутентификацию и записывает сеанс в Redis, используя spring-session (данные в Redis хранятся как XML).

Теперь мы хотим представить новое приложение (B), использующее spring-boot 2.2.6.RELEASE и spring-session Corn-RC1, которое должно использоваться, если пользователь вошел в (A) с помощью ROLE_ADMIN. Т.е. это можно рассматривать как очень грубый способ сделать единый вход. Пользователь должен никогда иметь возможность аутентификации в B (он хотел бы отключить аутентификацию, если это возможно), он должен только проверять, что существующий пользователь аутентифицирован в репозитории сеансов (redis) и имеет ROLE_ADMIN , И A, и B будут находиться в одном домене, поэтому файлы cookie будут распространяться браузером. Я пробовал разные способы заставить это работать, например:

@Configuration
@EnableWebSecurity
class ServiceBSpringSecurityConfig : WebSecurityConfigurerAdapter() {

    @Autowired
    fun configureGlobal(auth: AuthenticationManagerBuilder) {
        auth.inMemoryAuthentication()
    }

    override fun configure(http: HttpSecurity) {
        http
            .authorizeRequests()
                .anyRequest().hasRole("ADMIN")
                .and()
            .formLogin()
                .and()
            .httpBasic().disable()
    }
}

, но при этом отобразится экран входа по умолчанию:

Я также попытался полностью удалить эту часть:

@Autowired
fun configureGlobal(auth: AuthenticationManagerBuilder) {
    auth.inMemoryAuthentication()
}

, но тогда он сгенерирует пароль по умолчанию user и пароль, и он, кажется, не вызывает метод configure ( или конфигурация не работает независимо).

Как я могу решить эту проблему?

1 Ответ

1 голос
/ 07 апреля 2020

Вам нужно отключить formLogin и httBasic в приложении B и добавить фильтр перед фильтром аутентификации Spring AnonymousAuthenticationFilter или UsernamePasswordAuthenticationFilter. В пользовательском фильтре вы извлекаете cookie / header / token из объекта запроса и на основе этого обращаетесь в кэш redis для получения сведений о сеансе. Затем этот фильтр проверит сеанс и создаст объект типа org.springframework.security.core.Authentication и установит его в текущем SpringSecurityContext.

Ниже приведен код sudo для этого;

ServiceBSpringSecurityConfig

@Configuration
@EnableWebSecurity
public class ServiceBSpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
        .exceptionHandling().authenticationEntryPoint(authEntryPoint()).and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
        .httpBasic().disabled().and()
        .formLogin().disabled().and()
        .authorizeRequests().anyRequest().hasRole("ADMIN")

    http.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public AuthTokenFilter authTokenFilter() {
        return new AuthTokenFilter();
    }

    @Bean
    public AuthEntryPoint authEntryPoint() {
        return new AuthEntryPoint()
    }
}

AuthEntryPoint

public class AuthEntryPoint implements AuthenticationEntryPoint {

    private static final Logger logger = LoggerFactory.getLogger(AuthEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        // Very generic authEntryPoint which simply returns unauthorized
        // Could implement additional functionality of forwarding the Application A login-page
        logger.error("Unauthorized error: {}", authException.getMessage());
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
    }
}

AuthTokenFilter

public class AuthTokenFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // extract some sort of token or cookie value from request
        token = request.getHeader("Token");
        if (token != null) {
            // Validate the token by retrieving session from redis cache
            // Create org.springframework.security.core.Authentication from the token
            Authentication auth = authFactory.getAuthentication(token);

            // Set the spring security context with the auth
            SecurityContextHolder.getContext().setAuthentication(auth);
        } else {
            // Do something if token not present at all
        }
        // Continue to to filter chain
        filterChain.doFilter(request, response);
    }
}

Как уже упоминалось, это код sudo, поэтому некоторая корректировка может потребоваться. Однако общий смысл аутентификации на основе токенов остается прежним.

...