使用Keycloak的Spring应用程序返回401错误

gijlo24d  于 2023-09-29  发布在  Spring
关注(0)|答案(2)|浏览(148)

我试图通过Keycloak访问Spring应用程序,但总是收到401 Unauthorized错误。基本上我有一个聊天模块,它本身工作得很好,但是一旦我添加Keycloak,由于401错误,我无法访问应用程序。我已经遵循了大约3个教程,这些教程显示了与我所做的类似的事情,我仍然不知道我做错了什么。
以下是我的应用程序的配置:

keycloak: 
    enabled: true
    realm: myReal
    resource: myReal-api
    public-client: true
    bearer-only: true
    auth-server-url: http://localhost:8080/auth
    credentials:
      secret: 82eXXXXX-3XXX-4XXX-XXX7-287aXXXXXXXX
    principal-attribute: preferred_username
    cors: true

localhost:port/我有一个第一个接口(没有Keycloak安全),它有一个到我的服务的链接,这是localhost:port/index/{topicName}。现在,当我点击这个链接时,我应该得到Keycloak身份验证屏幕,但我得到了一个401错误。
我已经检查了请求的头部,将HttpServletRequest作为参数添加到displayMessage方法中,实际上可以在IDE的控制台中显示access_token和X-Auth-Token。但似乎当我点击那个链接时,它发送的请求没有令牌。
下面是我的控制器方法(我的Controller类用@Controller注解:

@GetMapping(path = "/")
    public String index() {
        return "external";
    }

    @GetMapping(path = "/index/{topicName}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public String displayMessages(Model model, 
            @PathVariable String topicName) {       

            //HttpHeaders headers = new HttpHeaders();

            //headers.set("Authorization", request.getHeader("Authorization"));
            //header = request.getHeader("Authorization");
            //System.out.println(" T O K E N "+request.getHeader("X-Auth-Token"));

            projectServiceImpl.findByName(topicName);
            List<Message> messages = messageServiceImpl.findAllMessagesByProjectName(topicName);
            model.addAttribute("topic", topicName);
            model.addAttribute("message",messages);

            return "index";
    }

我的Keycloak配置文件的灵感来自我读过的tuto,所以可能有一个我不知道的错误(不确定方法accesshasRole之间的区别是什么):

@Configuration
@ComponentScan(
        basePackageClasses = KeycloakSecurityComponents.class,
        excludeFilters = @ComponentScan.Filter(
                type = FilterType.REGEX, 
                pattern = "org.keycloak.adapters.springsecurity.management.HttpSessionManager"))
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

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

    @Bean
    public HttpSessionIdResolver httpSessionIdResolver() { //replace HttpSessionStrategy
        return HeaderHttpSessionIdResolver.xAuthToken();
    }

    //Registers the KeycloakAuthenticationProvider with the authentication manager.
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        try {
            SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
            grantedAuthorityMapper.setPrefix("ROLE_");
            grantedAuthorityMapper.setConvertToUpperCase(true);

            KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
            keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);

            auth.authenticationProvider(keycloakAuthenticationProvider());

        } catch(Exception ex)  {
            logger.error("SecurityConfig.configureGlobal: " + ex);

        }

         /*try {
                KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
                keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
                auth.authenticationProvider(keycloakAuthenticationProvider);
            }catch(Exception ex){
                logger.error("SecurityConfig.configureGlobal: " +ex);
            }*/
    }

    //Load Keycloak properties from service config-file
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    //Defines the session authentication strategy.
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        //Public or Confidential application keycloak/OpenID Connect client
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
        //Bearer mode only keycloak/OpenID Connect client without keycloak session -> stateless behavior
        //return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        super.configure(http);
        http.authorizeRequests()
        //BEGIN
            //USER -done to be tested

            .antMatchers(HttpMethod.GET,"/index**").access("hasAuthority('ADMIN')")
            .antMatchers(HttpMethod.GET,"/").access("hasAuthority('ADMIN')")
            .antMatchers(HttpMethod.GET,"/").access("hasAnyAuthority('ADMIN','MANAGER','EXPERT','STANDARD')")


            .anyRequest().authenticated() 

            .and()
            .cors()
            .and()
            .csrf().disable()
            //BEGIN Login/Logout
            .formLogin()
                .permitAll()//.successHandler(authenticationSuccessHandler) //
            .and()
            .logout()//.clearAuthentication(true) //Add .clearAuthentication(true) to logout()
                //.logoutUrl("/custom-logout")
                .addLogoutHandler(keycloakLogoutHandler())
                //.addLogoutHandler(new LogoutHandlerImpl())
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .permitAll();
            //END Login/Logout

        //BEGIN Session
        http
        .sessionManagement()
            //.sessionCreationPolicy(SessionCreationPolicy.ALWAYS) //BY default IF_REQUIRED
            .maximumSessions(1)
                .maxSessionsPreventsLogin(false) // if true generate an error when user login after reaching maximumSession (SessionAuthenticationStrategy rejected the authentication object / SessionAuthenticationException: Maximum sessions of 1 for this principal exceeded)
                //.expiredUrl("/auth/login")
                .sessionRegistry(sessionRegistry());   


    }

     @Bean
     @Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
     public AccessToken accessToken() {
         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
         return ((KeycloakSecurityContext) ((KeycloakAuthenticationToken) request.getUserPrincipal()).getCredentials()).getToken();
     }

    ///BEGIN session     
     @Bean
     public SessionRegistry sessionRegistry() {
        SessionRegistry sessionRegistry = new SessionRegistryImpl();
        return sessionRegistry;
     }

    @Bean
    public RegisterSessionAuthenticationStrategy registerSessionAuthStr( ) {
        return new RegisterSessionAuthenticationStrategy( sessionRegistry( ) );
    }

    // Register HttpSessionEventPublisher
    @Bean
    public static ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
        return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
    }

我真的不知道我还应该改变什么才能让它工作,但我相信那里一定有什么问题。但我想如果我可以有Keycloak认证屏幕时,试图访问我的服务,这将是好的。

6rvt4ljy

6rvt4ljy1#

我收到了同样的错误,需要仔细检查的一件事是,auth-server-url对于服务器和客户端获取令牌是相同的。
即如果一个是dns名称,一个是IP地址,则将不授权。(在我的情况下,我有localhost和127.0.0.1,所以授权失败)
服务器,src/main/resources/application.yml

Postman /客户:

3pvhb19x

3pvhb19x2#

这解决了我的问题。我在docker容器中使用,必须将两者都匹配到host.docker.internal

相关问题