безопасный пружинный ботинок с замком для ключей doenst return 403

#java #spring-boot #keycloak

Вопрос:

Я пытаюсь реализовать свое первое приложение для загрузки spring, защищенное с помощью ключа. Я совершенно новичок в обоих этих вопросах, но думаю, что до сих пор мне это удавалось. Я просматривал различные учебные пособия, но последним, с которым я работал, было следующее
Итак, я настроил приложение spring boot/hibernate, которое работает (в качестве доказательства концепции на данный момент). Итак, теперь я хочу обезопасить его с помощью ключа. То, что у меня есть, — это Controller.java

 @Controller // This means that this class is a Controller
@RequestMapping(path="/test") // This means URL's start with /demo (after Application path)
public class MainController {
 

    @Autowired
    GemhMainRepository repository;
    
    @Autowired
    CompaniesRepository gsisCompanies;
    
     @RequestMapping("/protected")
        public String protectedHello() {
           System.out.println("test"); return "Hello World, i was protected";
        }}
 

Main.java

 @SpringBootApplication(exclude = { SecurityAutoConfiguration.class })//do not create security credentials. we will use our own
public class Main {

     @Bean
        public KeycloakSpringBootConfigResolver keycloakSpringBootConfigResolver(){
            return new KeycloakSpringBootConfigResolver();
        }
     
    public static void main(String[] args) {
        SpringApplication.run(GsisApi.class, args);
    }
}
    
 

применение.свойства

     server.port=8080
        
    spring.jpa.hibernate.ddl-auto=none
        
    spring.datasource.url=jdbc:mysql://sqlHost:3306/Db?useUnicode=trueamp;characterEncoding=UTF-8amp;serverTimezone=UTCamp;useSSL=false
    spring.datasource.username=user
    spring.datasource.password=password
    spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
    spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    spring.jpa.show-sql=true  
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
    keycloak.auth-server-url=http://localhost:31063/auth
    keycloak.realm=testRealm
    keycloak.resource=test_client
    keycloak.public-client=true
    keycloak.security-constraints[0].authRoles[0]=user
    keycloak.security-constraints[0].securityCollections[0].patterns[0]=/test
    keycloak.principal-attribute=preferred_username
 

SecurityConfig.java

 @KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider  = keycloakAuthenticationProvider();
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .anyRequest().authenticated()
                ;
    }
    
}
 

Кажется, это работает нормально, когда я получаю жетон от keycloak,

 curl --data "grant_type=passwordamp;client_id=test_clientamp;username=useramp;password=passwordamp;client_secret=xxx" 'http://localhost:31063/auth/realms/testRealm/protocol/openid-connect/token'  
 

Я действительно получаю результат при запуске

  curl localhost:8080/test/protected -H "Authorization: bearer xxxxxxx " --insecure
 

Но когда я пытаюсь использовать его без жетона, я ничего не получаю. Даже сообщения, которое я печатаю на экране, нет. Я предполагаю, что мне нужно получить ошибку 403, чтобы сообщить пользователю, что он должен использовать какой-то токен.верно? какая-нибудь помощь?
Спасибо

Ответ №1:

Ладно, похоже, у меня получилось. По какой-то причине, которую я не могу понять, следование этим инструкциям, похоже, делает свое дело. Я создал
MyAuthenticationEntryPoint.java

 @ControllerAdvice
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
      throws IOException, ServletException {
    // 401
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
  }

  @ExceptionHandler (value = {AccessDeniedException.class})
  public void commence(HttpServletRequest request, HttpServletResponse response,
      AccessDeniedException accessDeniedException) throws IOException {
    // 403
    response.sendError(HttpServletResponse.SC_FORBIDDEN, "Authorization Failed : "   accessDeniedException.getMessage());
  }

  @ExceptionHandler (value = {Exception.class})
  public void commence(HttpServletRequest request, HttpServletResponse response,
      Exception exception) throws IOException {
     // 500
    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal Server Error : "   exception.getMessage());
  }

}
 

и добавил это к моему SecurityConfig.java

 http.exceptionHandling()
      .authenticationEntryPoint(new MyAuthenticationEntryPoint());
 

сделать это

 @KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider  = keycloakAuthenticationProvider();
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
        .antMatchers("/test/health/*").permitAll()
                .anyRequest().authenticated()
                ;
      http.exceptionHandling()
      .authenticationEntryPoint(new MyAuthenticationEntryPoint());
    }
}
 

также, добавив .antMatchers("/test/health/*").permitAll() I can call health без каких-либо учетных данных.
Я не на 100% уверен в том, что делаю. Так что, если вы увидите что-то не так, пожалуйста, дайте мне знать.