brief introduction
The user's request to access the client will first go through the filter chain configured by the client. The common filters are as follows:
- CAS Single Sign Out Filter-SingleSignOutFilter
- Realize single sign out and put it in the first position;
- CAS Validation Filter-Cas30ProxyReceivingTicketValidationFilter
- Be responsible for checking the Ticket
- Server address: URL prefix
- Client address needs to be specified: serverName
- CAS Authentication Filter-AuthenticationFilter
- Responsible for user authentication
- The server login address needs to be specified: casServerLoginUrl
- Client address needs to be specified: serverName
- CAS HttpServletRequest Wrapper Filter-HttpServletRequestWrapperFilter
- Is responsible for wrapping HttpServletRequest so that the login name of the login user can be obtained through the getRemoteUser() method of HttpServletRequest
The user's access request will be intercepted and processed by the above configured filter in turn;
SingleSignOutFilter
Firstly, it is processed by SingleSignOutFilter, which realizes the function of single sign out;
This is a user access request, not a login request. The filter will be released directly. The core code is as follows:
private static final SingleSignOutHandler HANDLER = new SingleSignOutHandler(); public void init(final FilterConfig filterConfig) throws ServletException { super.init(filterConfig); if (!isIgnoreInitConfiguration()) { // set a property setArtifactParameterName(getString(ConfigurationKeys.ARTIFACT_PARAMETER_NAME)); setLogoutParameterName(getString(ConfigurationKeys.LOGOUT_PARAMETER_NAME)); setFrontLogoutParameterName(getString(ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME)); setRelayStateParameterName(getString(ConfigurationKeys.RELAY_STATE_PARAMETER_NAME)); setCasServerUrlPrefix(getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX)); HANDLER.setArtifactParameterOverPost(getBoolean(ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST)); HANDLER.setEagerlyCreateSessions(getBoolean(ConfigurationKeys.EAGERLY_CREATE_SESSIONS)); } // handler initialization HANDLER.init(); // Set handler initialization completion ID handlerInitialized.set(true); } public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; /** * <p>Workaround for now for the fact that Spring Security will fail since it doesn't call {@link #init(javax.servlet.FilterConfig)}.</p> * <p>Ultimately we need to allow deployers to actually inject their fully-initialized {@link org.jasig.cas.client.session.SingleSignOutHandler}.</p> */ // Verify whether the initialization of the handler is completed if (!this.handlerInitialized.getAndSet(true)) { HANDLER.init(); } // Select whether to continue the filter chain according to the handler's processing if (HANDLER.process(request, response)) { filterChain.doFilter(servletRequest, servletResponse); } }
First, analyze the operation of setting properties. The getString method is used when setting properties. This method inherits from the parent class AbstractConfigurationFilter. The code is as follows:
protected final String getString(final ConfigurationKey<String> configurationKey) { return this.configurationStrategy.getString(configurationKey); }
getString uses this Configurationstrategy, continue to look at the assignment of this attribute. The code is as follows:
private static final String CONFIGURATION_STRATEGY_KEY = "configurationStrategy"; public void init(FilterConfig filterConfig) throws ServletException { // Get the configuration information of configurationStrategy from the configuration final String configurationStrategyName = filterConfig.getServletContext().getInitParameter(CONFIGURATION_STRATEGY_KEY); // Obtain the configurationstrategy instance according to the configuration information of configurationstrategy this.configurationStrategy = ReflectUtils.newInstance(ConfigurationStrategyName.resolveToConfigurationStrategy(configurationStrategyName)); // perform an initialization operation this.configurationStrategy.init(filterConfig, getClass()); } // ConfigurationStrategyName.java DEFAULT(LegacyConfigurationStrategyImpl.class), public static Class<? extends ConfigurationStrategy> resolveToConfigurationStrategy(final String value) { if (CommonUtils.isBlank(value)) { // If it is empty, return the default value legacyconfigurationstrategyimpl class return DEFAULT.configurationStrategyClass; } for (final ConfigurationStrategyName csn : values()) { if (csn.name().equalsIgnoreCase(value)) { return csn.configurationStrategyClass; } } try { final Class<?> clazz = Class.forName(value); if (ConfigurationStrategy.class.isAssignableFrom(clazz)) { return (Class<? extends ConfigurationStrategy>) clazz; } } catch (final ClassNotFoundException e) { LOGGER.error("Unable to locate strategy {} by name or class name. Using default strategy instead.", value, e); } return DEFAULT.configurationStrategyClass; }
If configurationStrategy is not configured in the configuration file, LegacyConfigurationStrategyImpl instance will be created by default. This type is the packaging of WebXmlConfigurationStrategyImpl and JNDI configurationstrategyimpl. WebXmlConfigurationStrategyImpl is preferred. The core code is as follows:
private final WebXmlConfigurationStrategyImpl webXmlConfigurationStrategy = new WebXmlConfigurationStrategyImpl(); private final JndiConfigurationStrategyImpl jndiConfigurationStrategy = new JndiConfigurationStrategyImpl(); public void init(FilterConfig filterConfig, Class<? extends Filter> filterClazz) { this.webXmlConfigurationStrategy.init(filterConfig, filterClazz); this.jndiConfigurationStrategy.init(filterConfig, filterClazz); } protected String get(final ConfigurationKey key) { // WebXmlConfigurationStrategyImpl is preferred final String value1 = this.webXmlConfigurationStrategy.get(key); if (CommonUtils.isNotBlank(value1)) { return value1; } return this.jndiConfigurationStrategy.get(key); }
Then analyze the implementation of SingleSignOutHandler, and its core code is as follows:
public boolean process(final HttpServletRequest request, final HttpServletResponse response) { if (isTokenRequest(request)) { // If the request carries a token // The Ticket information carried in this user access request will be returned here logger.trace("Received a token request"); // Record session recordSession(request); // Return true to continue the filter chain return true; } else if (isBackChannelLogoutRequest(request)) { // If post channel logout request // This user access request is not a login request and is not satisfied logger.trace("Received a back channel logout request"); // Destroy session destroySession(request); // Return false to terminate the filter chain return false; } else if (isFrontChannelLogoutRequest(request)) { // If front channel logout request // This user access request is not a login request and is not satisfied logger.trace("Received a front channel logout request"); // Destroy session destroySession(request); // redirection url to the CAS server final String redirectionUrl = computeRedirectionToServer(request); if (redirectionUrl != null) { // Redirect to CAS Server CommonUtils.sendRedirect(response, redirectionUrl); } // Return false to terminate the filter chain return false; } else { logger.trace("Ignoring URI for logout: {}", request.getRequestURI()); // It returns true by default and continues to execute the filter chain return true; } } private boolean isTokenRequest(final HttpServletRequest request) { return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters)); } private boolean isBackChannelLogoutRequest(final HttpServletRequest request) { return "POST".equals(request.getMethod()) && !isMultipartRequest(request) && CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters)); } private boolean isFrontChannelLogoutRequest(final HttpServletRequest request) { return "GET".equals(request.getMethod()) && CommonUtils.isNotBlank(this.casServerUrlPrefix) && CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.frontLogoutParameterName)); } private void recordSession(final HttpServletRequest request) { final HttpSession session = request.getSession(this.eagerlyCreateSessions); if (session == null) { logger.debug("No session currently exists (and none created). Cannot record session information for single sign out."); return; } final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters); logger.debug("Recording session for token {}", token); try { // Remove cached session information this.sessionMappingStorage.removeBySessionById(session.getId()); } catch (final Exception e) { // ignore if the session is already marked as invalid. Nothing we can do! } // Cache the current token and session sessionMappingStorage.addSessionById(token, session); } private void destroySession(final HttpServletRequest request) { final String logoutMessage; // front channel logout -> the message needs to be base64 decoded + decompressed if (isFrontChannelLogoutRequest(request)) { logoutMessage = uncompressLogoutMessage(CommonUtils.safeGetParameter(request, this.frontLogoutParameterName)); } else { logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters); } logger.trace("Logout request:\n{}", logoutMessage); final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex"); if (CommonUtils.isNotBlank(token)) { final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token); if (session != null) { final String sessionID = session.getId(); logger.debug("Invalidating session [{}] for token [{}]", sessionID, token); try { // Invalid session session.invalidate(); } catch (final IllegalStateException e) { logger.debug("Error invalidating session.", e); } // Log out this.logoutStrategy.logout(request); } } }
Cas30ProxyReceivingTicketValidationFilter
According to the filter configuration order, the next step is Cas30ProxyReceivingTicketValidationFilter, which is responsible for the verification of tickets. It is necessary to specify the server address casServerUrlPrefix and the client address serverName;
This user access request carries the Ticket information. The filter will verify the Ticket information. The core code is as follows:
public Cas30ProxyReceivingTicketValidationFilter() { // The supporting protocol is CAS3 super(Protocol.CAS3); // The ST validator uses Cas30ServiceTicketValidator this.defaultServiceTicketValidatorClass = Cas30ServiceTicketValidator.class; // The PT validator uses Cas30ProxyTicketValidator this.defaultProxyTicketValidatorClass = Cas30ProxyTicketValidator.class; }
Cas30ProxyReceivingTicketValidationFilter inherits from Cas20ProxyReceivingTicketValidationFilter and overrides this protocol,this.defaultServiceTicketValidatorClass and this The implementation of defaultproxyticketvalidatorclass and other processes are the same. Then analyze Cas20ProxyReceivingTicketValidationFilter;
The doFilter method of Cas20ProxyReceivingTicketValidationFilter inherits from the parent class AbstractTicketValidationFilter. AbstractTicketValidationFilter implements the algorithm template of doFilter method and provides algorithm details. preFilter, onSuccessfulValidation and onFailedValidation can be rewritten by subclasses to add additional processing logic. The core code is as follows:
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { // Pretreatment before filtration if (!preFilter(servletRequest, servletResponse, filterChain)) { return; } final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; // Get Ticket information from the request final String ticket = retrieveTicketFromRequest(request); // Whether there is Ticket information // This user access request carries Ticket information if (CommonUtils.isNotBlank(ticket)) { logger.debug("Attempting to validate ticket: {}", ticket); try { // If there is Ticket information, it needs to be verified // Verify the Ticket information carried in this user access request final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response)); logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName()); // If no exception is thrown, the verification is successful // Put the verification results into the request to facilitate the next acquisition request.setAttribute(CONST_CAS_ASSERTION, assertion); if (this.useSession) { // In the case of using session, the verification result is in the session attribute of the method request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion); } // Process when the verification is successful onSuccessfulValidation(request, response, assertion); if (this.redirectAfterValidation) { // The default value is true. Redirection is required after verification // After resetting, there will be Assertion information in the request, so you can use the AuthenticationFilter // But the Ticket information will not be carried, so you can directly use cas30proxyreceiving Ticket validation filter logger.debug("Redirecting after successful ticket validation."); response.sendRedirect(constructServiceUrl(request, response)); return; } } catch (final TicketValidationException e) { logger.debug(e.getMessage(), e); // Process when verification fails onFailedValidation(request, response); if (this.exceptionOnValidationFailure) { throw new ServletException(e); } // Return httpservletresponse SC_ Forward response response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); return; } } // Execute filter chain filterChain.doFilter(request, response); } protected boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { return true; } protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response, final Assertion assertion) { // nothing to do here. } protected void onFailedValidation(final HttpServletRequest request, final HttpServletResponse response) { // nothing to do here. } protected String retrieveTicketFromRequest(final HttpServletRequest request) { // Get "ticket" information from the request return CommonUtils.safeGetParameter(request, this.protocol.getArtifactParameterName()); }
For this The redirectaftervalidation attribute needs to be explained. This value determines whether redirection is required after Ticket verification is successful:
- If redirection is performed, the new request will not carry the Ticket information, and the new request will not need to be verified; However, since AuthenticationFilter obtains authentication results from Session, to redirect, it must support the use of Session, that is, this Usesession is true;
- If redirection is not performed, the original request will also carry the Ticket information, and the Ticket information obtained by AuthenticationFilter will be released;
So this The code related to the assignment of redirectaftervalidation property is as follows:
private boolean redirectAfterValidation = true; protected void initInternal(final FilterConfig filterConfig) throws ServletException { setExceptionOnValidationFailure(getBoolean(ConfigurationKeys.EXCEPTION_ON_VALIDATION_FAILURE)); setRedirectAfterValidation(getBoolean(ConfigurationKeys.REDIRECT_AFTER_VALIDATION)); setUseSession(getBoolean(ConfigurationKeys.USE_SESSION)); if (!this.useSession && this.redirectAfterValidation) { // If Session is not used, redirection to the client after Ticket verification is not supported logger.warn("redirectAfterValidation parameter may not be true when useSession parameter is false. Resetting it to false in order to prevent infinite redirects."); setRedirectAfterValidation(false); } setTicketValidator(getTicketValidator(filterConfig)); super.initInternal(filterConfig); }
Cas20ProxyReceivingTicketValidationFilter rewrites preFilter to preprocess requests in proxy mode. The code is as follows:
protected final boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; final String requestUri = request.getRequestURI(); // Is it a proxy request // This user access request is not a proxy request if (CommonUtils.isEmpty(this.proxyReceptorUrl) || !requestUri.endsWith(this.proxyReceptorUrl)) { return true; } try { CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage); } catch (final RuntimeException e) { logger.error(e.getMessage(), e); throw e; } return false; }
AbstractTicketValidationFilter. This. Is used in dofilter Ticketvalidator is used to verify Ticket information. Cas30ServiceTicketValidator is used by default in Cas30ProxyReceivingTicketValidationFilter. Its validate method inherits from the parent class AbstractUrlBasedTicketValidator. AbstractUrlBasedTicketValidator implements the algorithm template of the validate method and provides algorithm details. retrieveResponseFromServer parseResponseFromServer is implemented by subclasses, and the code is as follows:
public final Assertion validate(final String ticket, final String service) throws TicketValidationException { // Build verification request URL final String validationUrl = constructValidationUrl(ticket, service); logger.debug("Constructing validation url: {}", validationUrl); try { // Send the verification request and get the server response logger.debug("Retrieving response from server."); final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket); // If the returned result is empty, the verification fails if (serverResponse == null) { throw new TicketValidationException("The CAS server returned no response."); } logger.debug("Server response: {}", serverResponse); // Parsing server response return parseResponseFromServer(serverResponse); } catch (final MalformedURLException e) { throw new TicketValidationException(e); } } protected abstract String retrieveResponseFromServer(URL validationUrl, String ticket); protected abstract Assertion parseResponseFromServer(final String response) throws TicketValidationException; protected final String constructValidationUrl(final String ticket, final String serviceUrl) { final Map<String, String> urlParameters = new HashMap<String, String>(); logger.debug("Placing URL parameters in map."); // Put Ticket information urlParameters.put("ticket", ticket); // Put in Service information urlParameters.put("service", serviceUrl); // If renew is included if (this.renew) { urlParameters.put("renew", "true"); } logger.debug("Calling template URL attribute map."); // Populate other attributes populateUrlAttributeMap(urlParameters); logger.debug("Loading custom parameters from configuration."); if (this.customParameters != null) { urlParameters.putAll(this.customParameters); } final String suffix = getUrlSuffix(); final StringBuilder buffer = new StringBuilder(urlParameters.size() * 10 + this.casServerUrlPrefix.length() + suffix.length() + 1); int i = 0; // Construct URL buffer.append(this.casServerUrlPrefix); if (!this.casServerUrlPrefix.endsWith("/")) { buffer.append("/"); } buffer.append(suffix); for (Map.Entry<String, String> entry : urlParameters.entrySet()) { final String key = entry.getKey(); final String value = entry.getValue(); if (value != null) { buffer.append(i++ == 0 ? "?" : "&"); buffer.append(key); buffer.append("="); final String encodedValue = encodeUrl(value); buffer.append(encodedValue); } } return buffer.toString(); }
AbstractCasProtocolUrlBasedTicketValidator implements the algorithm template of retrieveResponseFromServer for algorithm details. The code is as follows:
protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) { // With CommonUtils return CommonUtils.getResponseFromServer(validationUrl, getURLConnectionFactory(), getEncoding()); } // CommonUtils.java public static String getResponseFromServer(final URL constructedUrl, final HttpURLConnectionFactory factory, final String encoding) { HttpURLConnection conn = null; InputStreamReader in = null; try { conn = factory.buildHttpURLConnection(constructedUrl.openConnection()); if (CommonUtils.isEmpty(encoding)) { in = new InputStreamReader(conn.getInputStream()); } else { in = new InputStreamReader(conn.getInputStream(), encoding); } // Get response final StringBuilder builder = new StringBuilder(255); int byteRead; while ((byteRead = in.read()) != -1) { builder.append((char) byteRead); } // Return response return builder.toString(); } catch (final Exception e) { LOGGER.error(e.getMessage(), e); throw new RuntimeException(e); } finally { closeQuietly(in); if (conn != null) { conn.disconnect(); } } }
Cas20ServiceTicketValidator implements the algorithm template of the algorithm detail parseResponseFromServer, and provides the algorithm detail customParseResponse for subclass rewriting to add additional processing logic. The code is as follows:
protected final Assertion parseResponseFromServer(final String response) throws TicketValidationException { final String error = XmlUtils.getTextForElement(response, "authenticationFailure"); if (CommonUtils.isNotBlank(error)) { // There is an error message throw new TicketValidationException(error); } final String principal = XmlUtils.getTextForElement(response, "user"); final String proxyGrantingTicketIou = XmlUtils.getTextForElement(response, "proxyGrantingTicket"); // Get PGT information final String proxyGrantingTicket; if (CommonUtils.isBlank(proxyGrantingTicketIou) || this.proxyGrantingTicketStorage == null) { proxyGrantingTicket = null; } else { proxyGrantingTicket = this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou); } // Verify principal information if (CommonUtils.isEmpty(principal)) { throw new TicketValidationException("No principal was found in the response from the CAS server."); } // Encapsulate Assertion information final Assertion assertion; final Map<String, Object> attributes = extractCustomAttributes(response); if (CommonUtils.isNotBlank(proxyGrantingTicket)) { final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes, proxyGrantingTicket, this.proxyRetriever); assertion = new AssertionImpl(attributePrincipal); } else { assertion = new AssertionImpl(new AttributePrincipalImpl(principal, attributes)); } // Support subclass customization and parsing Assertion information customParseResponse(response, assertion); return assertion; } protected Map<String, Object> extractCustomAttributes(final String xml) { final SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); spf.setValidating(false); try { final SAXParser saxParser = spf.newSAXParser(); final XMLReader xmlReader = saxParser.getXMLReader(); final CustomAttributeHandler handler = new CustomAttributeHandler(); xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(new StringReader(xml))); return handler.getAttributes(); } catch (final Exception e) { logger.error(e.getMessage(), e); return Collections.emptyMap(); } } protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException { // nothing to do }
AuthenticationFilter
Next is the AuthenticationFilter, which is responsible for user authentication. You need to specify the server login address casServerLoginUrl and the client address serverName;
After the user access request is processed by Cas30ProxyReceivingTicketValidationFilter, there is Ticket or Assertion information, so it will be released by the filter. The core code is as follows:
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; // This user access request does not involve if (isRequestUrlExcluded(request)) { logger.debug("Request is ignored."); // If the request is excluded, the filter chain is executed directly filterChain.doFilter(request, response); return; } // Try to get Assertion information from Session final HttpSession session = request.getSession(false); final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null; // This user access request is processed by Cas30ProxyReceivingTicketValidationFilter // If it is redirected back, the Assertion information will be carried // If it is not redirected back, there will be Ticket information // The Session may not be used, and the Assertion information cannot be obtained, so it cannot be redirected if (assertion != null) { // If Assertion exists, it can be released directly without authentication // If the request is redirected back, it will be returned from here filterChain.doFilter(request, response); return; } // Construct this client URL final String serviceUrl = constructServiceUrl(request, response); // Get Ticket information from request final String ticket = retrieveTicketFromRequest(request); final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl); // This user access request is processed by Cas30ProxyReceivingTicketValidationFilter // If it is not redirected, there will be Ticket information if (CommonUtils.isNotBlank(ticket) || wasGatewayed) { // If there is Ticket information, you can release it directly without authentication // This request will be returned from here if it is redirected back alive filterChain.doFilter(request, response); return; } final String modifiedServiceUrl; logger.debug("no ticket and no assertion found"); if (this.gateway) { logger.debug("setting gateway attribute in session"); modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl); } else { modifiedServiceUrl = serviceUrl; } logger.debug("Constructed service url: {}", modifiedServiceUrl); final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway); // Redirect to the server for login authentication logger.debug("redirecting to \"{}\"", urlToRedirectTo); this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo); }
HttpServletRequestWrapperFilter
The next step is HttpServletRequestWrapperFilter, which is responsible for packaging HttpServletRequest, so that the login name of the login user can be obtained through getRemoteUser() method of HttpServletRequest. The core code is as follows:
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { // Obtain the Principal information from the request or session final AttributePrincipal principal = retrievePrincipalFromSessionOrRequest(servletRequest); // Wrap ServletRequest and add Principal information filterChain.doFilter(new CasHttpServletRequestWrapper((HttpServletRequest) servletRequest, principal), servletResponse); } /** * Inner class * It inherits from HttpServletRequestWrapper and encapsulates the Principal information */ final class CasHttpServletRequestWrapper extends HttpServletRequestWrapper { private final AttributePrincipal principal; CasHttpServletRequestWrapper(final HttpServletRequest request, final AttributePrincipal principal) { super(request); this.principal = principal; } public Principal getUserPrincipal() { return this.principal; } public String getRemoteUser() { return principal != null ? this.principal.getName() : null; } public boolean isUserInRole(final String role) { if (CommonUtils.isBlank(role)) { logger.debug("No valid role provided. Returning false."); return false; } if (this.principal == null) { logger.debug("No Principal in Request. Returning false."); return false; } if (CommonUtils.isBlank(roleAttribute)) { logger.debug("No Role Attribute Configured. Returning false."); return false; } final Object value = this.principal.getAttributes().get(roleAttribute); if (value instanceof Collection<?>) { for (final Object o : (Collection<?>) value) { if (rolesEqual(role, o)) { logger.debug("User [{}] is in role [{}]: true", getRemoteUser(), role); return true; } } } final boolean isMember = rolesEqual(role, value); logger.debug("User [{}] is in role [{}]: {}", getRemoteUser(), role, isMember); return isMember; } /** * Determines whether the given role is equal to the candidate * role attribute taking into account case sensitivity. * * @param given Role under consideration. * @param candidate Role that the current user possesses. * * @return True if roles are equal, false otherwise. */ private boolean rolesEqual(final String given, final Object candidate) { return ignoreCase ? given.equalsIgnoreCase(candidate.toString()) : given.equals(candidate); } }
So far, the processing flow of the user access request carrying the Ticket is completed.