Using JAAS with Tomcat

本文介绍如何将Java Authentication and Authorization Service (JAAS)与Tomcat集成,实现更灵活的安全性和授权管理。通过自定义登录模块和策略,可以增强应用程序的安全控制。

back to index

Using JAAS with Tomcat

Although it is possible to use JAAS within Tomcat as an authentication mechanism (JAASRealm), the flexibility of the JAAS framework is lost once the user is authenticated. This is because the principals are used to denote the concepts of "user" and "role", and are no longer available in the security context in which the webapp is executed. The result of the authentication is available only through request.getRemoteUser() and request.isUserInRole().

This reduces the JAAS framework for authorization purposes to a simple user/role system that loses its connection with the Java Security Policy. This tutorial's purpose is to put a full-blown JAAS authorisation implementation in place, using a few tricks to deal with some of Tomcat's idiosyncrasies.

Basic Design

The goal of the exercise is to be able to wrap the execution of our servlets/jsps in our own JAAS implementation, which allows us to enforce access control with a simple call in our code to AccessController.checkPermission(MyOwnPermission). We achieve this by wrapping each request in a security filter. This is configured in our web.xml. Most of this is covered in widely available JAAS tutorials on the net, so we only have some code fragments here.

web.xml

  <filter>
    <filter-name>SecurityFilter</filter-name>
    <filter-class>security.SecurityFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>SecurityFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- Define a Security Constraint on this Application -->
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Entire Application</web-resource-name>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>authenticateduser</role-name>
    </auth-constraint>
  </security-constraint>

  <!-- Define the Login Configuration for this Application -->
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>My Realm</realm-name>
  </login-config>

login.conf

To start tomcat in security mode we call startup.sh with the -security flag. Furthermore we need to call our login module by setting $JVM_OPTS to something like -Djava.security.auth.login.config=/usr/local/tomcat/webapps/jaastest/WEB-INF/login.conf

Jaas {
    security.HttpLoginModule required debug=true;
};

HttpLoginModule

public class HttpLoginModule implements LoginModule {
...
    public boolean commit() throws LoginException {
        if (debug)
        	System.err.println("HttpLoginModule: Commit");

        if (!succeeded) {
            // We didn't authenticate the user, but someone else did.
            // Clean up our state, but don't add our principal to
            // the subject
            userName = null;
            return false;
        }
        
        assignPrincipal(new UserPrincipal(userName));

	//Based on the username, we can assign principals here
        //Some examples for test....
        assignPrincipal(new RolePrincipal("authenticateduser")); 
        assignPrincipal(new RolePrincipal("administrator")); 
        assignPrincipal(new CustomPrincipal("company1")); 
       
        // Clean up our internal state
        userName = null;
        commitSucceeded = true;
        return true;
    }

    private void assignPrincipal(Principal p)
    {
	// Make sure we dont add duplicate principals
	if (!subject.getPrincipals().contains(p)) {
	    subject.getPrincipals().add(p);
	}
	    
	if(debug) System.out.println("Assigned principal "+p.getName()+" of type "+ p.getClass().getName() +" to user "+userName);
    }

HttpAuthCallbackHandler

We have Tomcat or Apache do the authentication for us, so we just rely on request.getRemoteUser() to tell us the user name. Feel free to extend...

class HttpAuthCallbackHandler implements CallbackHandler {

  private String userName;

  public HttpAuthCallbackHandler (HttpServletRequest request) {
    userName = request.getRemoteUser();
    System.out.println("Remote user is: " + request.getRemoteUser());
  }

  public void handle(Callback[] cb) throws IOException, UnsupportedCallbackException {
	
    for (int i = 0; i < cb.length; i++) {
      if (cb[i] instanceof NameCallback) {
        NameCallback nc = (NameCallback) cb[i];
        nc.setName(userName);
      } else throw new
        UnsupportedCallbackException(cb[i], "HttpAuthCallbackHandler");
    }
  }
}

CustomPolicy

We install a Policy that adds Custom Permissions to the existing PermissionCollection

public class CustomPolicy extends Policy {

	private Policy deferredPolicy;
	
	public CustomPolicy(Policy p) {
		deferredPolicy = p;
	}

	public PermissionCollection getPermissions(CodeSource cs) {
		PermissionCollection pc = deferredPolicy.getPermissions(cs);
		System.out.println("getPermissions was called for codesource");
		return pc;
	}

	public PermissionCollection getPermissions(ProtectionDomain domain) {

		PermissionCollection pc = deferredPolicy.getPermissions(domain);
		System.out.println("getPermissions was called for domain");
		Principal[] principals = domain.getPrincipals();
		System.out.println("retrieved " + principals.length + " principals");
		
		for (int i=0; i< principals.length; i++) {
			
			Principal p = principals[i];
			System.out.println("This is principal" + p);
			CustomPermission[] pms = null;
			if (p instanceof CustomPrincipal ) {
				
				System.out.println(p.getName()  + " is a CustomPrincipal");
				
				// Get the permissions belonging to the principal here.
				// Here we just add an example permission
				CustomPermission[] test =  { new CustomPermission("AccessToCompany1Building") };
				pms = test;
			} else {
			  System.out.println(p.getName()  + " is not a CustomPrincipal");
			}

			// Nothing to do
			if (pms == null)  continue;

			for(int j=0; j< pms.length; j++) {
				System.out.println("Adding permission = " + pms[j]);
				pc.add(pms[j]);
			}

		}

		System.out.println(pc);
		return pc;
	}

	public void refresh() {
		deferredPolicy.refresh();
	}
}

SecurityFilter

This is the core of our solution. We install our CustomPolicy here (note that this is definitely not redeployment-safe!). Note that we the feed our authenticated subject into the session. Tomcat then uses this subject when calling your servlets. This is the trick that makes the whole thing work; without it, the Subject is lost.

public class SecurityFilter implements Filter {
	
	public void init(FilterConfig config) throws ServletException {
		Policy orgPolicy = Policy.getPolicy();

		if (orgPolicy instanceof CustomPolicy) {
			// we already did this once upon a time..
			System.out.println("Policy is a CustomPolicy,we already did this once upon a time");
		} else {
			Policy.setPolicy(new CustomPolicy(orgPolicy));
			System.out.println("Policy is not a CustomPolicy");
		}
	}
	
	public void destroy() {
		//config = null;
	}
	
	public void doFilter(ServletRequest sreq, ServletResponse sres,
						 FilterChain chain) throws IOException, ServletException {
		System.out.println("Starting SecurityFilter.doFilter");
		
		HttpServletResponse response = (HttpServletResponse)sres;
		HttpServletRequest request = (HttpServletRequest)sreq;
		
		HttpSession session = request.getSession(true);
		Subject subject = (Subject)session.getAttribute("javax.security.auth.subject");
		
		if (subject == null) {
			subject = new Subject();
		}
		
		session.setAttribute("javax.security.auth.subject", subject);
		
		LoginContext lc = null;
		try {
			lc = new LoginContext("Jaas", subject, new HttpAuthCallbackHandler(request));
			System.out.println("established new logincontext");
		} catch (LoginException le) {
			le.printStackTrace();
			response.sendError(HttpServletResponse.SC_FORBIDDEN, request.getRequestURI());
			return;
		} 

		try {
			lc.login();
			// if we return with no exception, authentication succeeded
		} catch (Exception e) {
			System.out.println("Login failed: " + e);
			response.sendError(HttpServletResponse.SC_FORBIDDEN, request.getRequestURI());
			return;
		}
		
		try {
			System.out.println("Subject is " + lc.getSubject());
			chain.doFilter(request, response);
		} catch(SecurityException se) {
			response.sendError(HttpServletResponse.SC_FORBIDDEN, request.getRequestURI());
		}
	}
}

Testing it out

If you want to test your subject, a simple JSP like the one below is a great help

<%@page import="java.security.*" %>
<%@page import="javax.security.auth.*" %>
<html>
<h1>Hello World!</h1>

<pre>
<% Subject subject = Subject.getSubject(AccessController.getContext()); %> 


<b>Subject</b> = <%= subject %>

-----------------------------------------------

<b>RemoteUser</b> = <%= request.getRemoteUser() %>

-----------------------------------------------

<%
	out.print("Is user in role \"authenticateduser\"?: ");
	if (request.isUserInRole("authenticateduser")) {
		out.println("yes");
	} else {
		out.println("no");
	}
%> 

-----------------------------------------------

<b>Session Contents</b>
<% 
	java.util.Enumeration atts = session.getAttributeNames();
	while (atts.hasMoreElements()) {
		String elem = (String)atts.nextElement();
		out.println(elem + " -> " + session.getAttribute(elem));
		out.println( );
	}
%>


</pre>

</html>

Copyright (c) 2004 Michiel Toneman : rot13(zvpuvry@xbcm.bet)
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. The full text of the license is found at: http://www.gnu.org/copyleft/fdl.html

2025-11-05 15:19:23.442 [main] INFO o.w.a.web.api.WebApiApplication - Starting WebApiApplication using Java 17.0.16 with PID 24352 (D:\world_aquatics_0922\world-aquatics-api\world-aquatics-web-api\target\classes started by LZY in D:\world_aquatics_0922) 2025-11-05 15:19:23.444 [main] INFO o.w.a.web.api.WebApiApplication - No active profile set, falling back to 1 default profile: "default" 2025-11-05 15:19:25.147 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode 2025-11-05 15:19:25.149 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Elasticsearch repositories in DEFAULT mode. 2025-11-05 15:19:25.166 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 13 ms. Found 0 Elasticsearch repository interfaces. 2025-11-05 15:19:25.171 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode 2025-11-05 15:19:25.171 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Reactive Elasticsearch repositories in DEFAULT mode. 2025-11-05 15:19:25.173 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 1 ms. Found 0 Reactive Elasticsearch repository interfaces. 2025-11-05 15:19:25.183 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode 2025-11-05 15:19:25.184 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode. 2025-11-05 15:19:25.192 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 1 ms. Found 0 Redis repository interfaces. 2025-11-05 15:19:25.879 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8081 (http) 2025-11-05 15:19:25.891 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat] 2025-11-05 15:19:25.891 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] 2025-11-05 15:19:25.960 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext 2025-11-05 15:19:25.960 [main] INFO o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 2480 ms 2025-11-05 15:19:25.981 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'jwt.secret' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:25.982 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'jwt.expire' in PropertySource 'environmentProperties' with value of type String Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. Can not find table primary key in Class: "org.world.aquatics.common.domain.SysRoleMenuTbl". 2025-11-05 15:19:26.770 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class org.world.aquatics.common.domain.SysRoleMenuTbl ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method. Can not find table primary key in Class: "org.world.aquatics.common.domain.SysUserRoleTbl". 2025-11-05 15:19:26.800 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class org.world.aquatics.common.domain.SysUserRoleTbl ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method. Initialization Sequence datacenterId:11 workerId:14 _ _ |_ _ _|_. ___ _ | _ | | |\/|_)(_| | |_\ |_)||_|_\ / | 3.5.5 2025-11-05 15:19:27.193 [main] INFO o.w.a.s.config.ElasticsearchConfig - Elasticsearch 配置加载: host={172.18.56.53}, port={9201} 2025-11-05 15:19:27.520 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'spring.kafka.bootstrap-servers' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.521 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'spring.kafka.consumer.group-id' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.786 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'springdoc.api-docs.path' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.786 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'springdoc.api-docs.path' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.789 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'springdoc.api-docs.path' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.790 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'springdoc.api-docs.path' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.791 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'springdoc.swagger-ui.path' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.791 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'springdoc.swagger-ui.path' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.791 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'springdoc.api-docs.path' in PropertySource 'environmentProperties' with value of type String 2025-11-05 15:19:27.902 [main] INFO o.s.s.web.DefaultSecurityFilterChain - Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@476a2819, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@279ab15e, org.springframework.security.web.context.SecurityContextHolderFilter@3bbc47c9, org.springframework.security.web.header.HeaderWriterFilter@62a81453, org.springframework.web.filter.CorsFilter@571c2ed8, org.springframework.security.web.authentication.logout.LogoutFilter@188ae8d2, org.world.aquatics.web.api.sso.JwtRequestFilter@2055833f, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@144dc2f7, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@403cff1c, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5810772a, org.springframework.security.web.session.SessionManagementFilter@690677de, org.springframework.security.web.access.ExceptionTranslationFilter@102aa5fc, org.springframework.security.web.access.intercept.AuthorizationFilter@eddc9bb] 2025-11-05 15:19:28.324 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port 8081 (http) with context path '' 2025-11-05 15:19:28.363 [main] INFO o.a.k.c.consumer.ConsumerConfig - ConsumerConfig values: allow.auto.create.topics = true auto.commit.interval.ms = 5000 auto.include.jmx.reporter = true auto.offset.reset = earliest bootstrap.servers = [172.18.56.53:9092] check.crcs = true client.dns.lookup = use_all_dns_ips client.id = consumer-world-aquatics-group-1 client.rack = connections.max.idle.ms = 540000 default.api.timeout.ms = 60000 enable.auto.commit = false exclude.internal.topics = true fetch.max.bytes = 52428800 fetch.max.wait.ms = 500 fetch.min.bytes = 1 group.id = world-aquatics-group group.instance.id = null heartbeat.interval.ms = 3000 interceptor.classes = [] internal.leave.group.on.close = true internal.throw.on.fetch.stable.offset.unsupported = false isolation.level = read_uncommitted key.deserializer = class org.apache.kafka.common.serialization.StringDeserializer max.partition.fetch.bytes = 1048576 max.poll.interval.ms = 300000 max.poll.records = 500 metadata.max.age.ms = 300000 metric.reporters = [] metrics.num.samples = 2 metrics.recording.level = INFO metrics.sample.window.ms = 30000 partition.assignment.strategy = [class org.apache.kafka.clients.consumer.RangeAssignor, class org.apache.kafka.clients.consumer.CooperativeStickyAssignor] receive.buffer.bytes = 65536 reconnect.backoff.max.ms = 1000 reconnect.backoff.ms = 50 request.timeout.ms = 30000 retry.backoff.ms = 100 sasl.client.callback.handler.class = null sasl.jaas.config = null sasl.kerberos.kinit.cmd = /usr/bin/kinit sasl.kerberos.min.time.before.relogin = 60000 sasl.kerberos.service.name = null sasl.kerberos.ticket.renew.jitter = 0.05 sasl.kerberos.ticket.renew.window.factor = 0.8 sasl.login.callback.handler.class = null sasl.login.class = null sasl.login.connect.timeout.ms = null sasl.login.read.timeout.ms = null sasl.login.refresh.buffer.seconds = 300 sasl.login.refresh.min.period.seconds = 60 sasl.login.refresh.window.factor = 0.8 sasl.login.refresh.window.jitter = 0.05 sasl.login.retry.backoff.max.ms = 10000 sasl.login.retry.backoff.ms = 100 sasl.mechanism = GSSAPI sasl.oauthbearer.clock.skew.seconds = 30 sasl.oauthbearer.expected.audience = null sasl.oauthbearer.expected.issuer = null sasl.oauthbearer.jwks.endpoint.refresh.ms = 3600000 sasl.oauthbearer.jwks.endpoint.retry.backoff.max.ms = 10000 sasl.oauthbearer.jwks.endpoint.retry.backoff.ms = 100 sasl.oauthbearer.jwks.endpoint.url = null sasl.oauthbearer.scope.claim.name = scope sasl.oauthbearer.sub.claim.name = sub sasl.oauthbearer.token.endpoint.url = null security.protocol = PLAINTEXT security.providers = null send.buffer.bytes = 131072 session.timeout.ms = 45000 socket.connection.setup.timeout.max.ms = 30000 socket.connection.setup.timeout.ms = 10000 ssl.cipher.suites = null ssl.enabled.protocols = [TLSv1.2, TLSv1.3] ssl.endpoint.identification.algorithm = https ssl.engine.factory.class = null ssl.key.password = null ssl.keymanager.algorithm = SunX509 ssl.keystore.certificate.chain = null ssl.keystore.key = null ssl.keystore.location = null ssl.keystore.password = null ssl.keystore.type = JKS ssl.protocol = TLSv1.3 ssl.provider = null ssl.secure.random.implementation = null ssl.trustmanager.algorithm = PKIX ssl.truststore.certificates = null ssl.truststore.location = null ssl.truststore.password = null ssl.truststore.type = JKS value.deserializer = class org.apache.kafka.common.serialization.StringDeserializer 2025-11-05 15:19:28.445 [main] INFO o.a.kafka.common.utils.AppInfoParser - Kafka version: 3.6.0 2025-11-05 15:19:28.446 [main] INFO o.a.kafka.common.utils.AppInfoParser - Kafka commitId: 60e845626d8a465a 2025-11-05 15:19:28.446 [main] INFO o.a.kafka.common.utils.AppInfoParser - Kafka startTimeMs: 1762327168444 2025-11-05 15:19:28.448 [main] INFO o.s.k.c.DefaultKafkaConsumerFactory$ExtendedKafkaConsumer - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Subscribed to topic(s): world-aquatics-topic 2025-11-05 15:19:28.462 [main] INFO o.w.a.web.api.WebApiApplication - Started WebApiApplication in 5.359 seconds (process running for 7.57) === Config File Loading Information === Active profiles: [] Default profiles: [default] 2025-11-05 15:19:28.464 [main] INFO o.w.a.web.api.WebApiApplication - WebApiApplication启动成功! 2025-11-05 15:19:28.857 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO org.apache.kafka.clients.Metadata - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Cluster ID: seNzt9cQRVW5qmRcTDdWIw 2025-11-05 15:19:28.858 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Discovered group coordinator 172.18.56.53:9092 (id: 2147483646 rack: null) 2025-11-05 15:19:28.859 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] (Re-)joining group 2025-11-05 15:19:28.872 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Request joining group due to: need to re-join with the given member-id: consumer-world-aquatics-group-1-3cdba838-80cb-4630-9f17-37c5002ee331 2025-11-05 15:19:28.873 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Request joining group due to: rebalance failed due to 'The group member needs to have a valid member id before actually entering a consumer group.' (MemberIdRequiredException) 2025-11-05 15:19:28.873 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] (Re-)joining group 2025-11-05 15:19:31.882 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Successfully joined group with generation Generation{generationId=1, memberId='consumer-world-aquatics-group-1-3cdba838-80cb-4630-9f17-37c5002ee331', protocol='range'} 2025-11-05 15:19:31.887 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Finished assignment for group at generation 1: {consumer-world-aquatics-group-1-3cdba838-80cb-4630-9f17-37c5002ee331=Assignment(partitions=[world-aquatics-topic-0])} 2025-11-05 15:19:31.901 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Successfully synced group in generation Generation{generationId=1, memberId='consumer-world-aquatics-group-1-3cdba838-80cb-4630-9f17-37c5002ee331', protocol='range'} 2025-11-05 15:19:31.901 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Notifying assignor about the new Assignment(partitions=[world-aquatics-topic-0]) 2025-11-05 15:19:31.902 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Adding newly assigned partitions: world-aquatics-topic-0 2025-11-05 15:19:31.910 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.ConsumerCoordinator - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Found no committed offset for partition world-aquatics-topic-0 2025-11-05 15:19:31.918 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.c.i.SubscriptionState - [Consumer clientId=consumer-world-aquatics-group-1, groupId=world-aquatics-group] Resetting offset for partition world-aquatics-topic-0 to position FetchPosition{offset=0, offsetEpoch=Optional.empty, currentLeader=LeaderAndEpoch{leader=Optional[172.18.56.53:9092 (id: 1 rack: null)], epoch=0}}. 2025-11-05 15:19:31.919 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.s.k.l.KafkaMessageListenerContainer - world-aquatics-group: partitions assigned: [world-aquatics-topic-0] 这一段日志输出表明了什么问题?需要怎么去解决这个问题?
最新发布
11-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值