I am attempting to add BASIC authentication to my RESTful web-service. Currently I have BASIC authentication for an Apache Tomcat 6.0 server, but I need to deploy my web-service on a WebSphere application server ver. 6.1 as well and I am having problems getting BASIC authentication running on WebSphere.
Is there a way in Java to check the authentication headers of an HTTP request and if the username/password provided (in Base64 encoding) doesn't match a known account force the user to enter in a new username/password?
I have tried implementing Spring Security, but since my project was made entirely without Spring it has been a huge pain trying to get it to work, and I am attempting to find a simple solution to my rather simple problem.
Technologies that I am currently using include: Java, Jersey/JAX-RS, Eclipse with Maven plugin.
You should be able to setup a servlet filter which gets executed before your REST handlers, inspects the "Authorization" request header, base 64 decodes it, extracts the username and password, and verifies. Something like this:
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain) {
if (request instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) req;
String authHeader = Base64.decode(request.getHeader("Authorization"));
String creds[] = authHeader.split(":");
String username = creds[0], password = creds[1];
// Verify the credentials here...
if (authorized) {
chain.doFilter(req, res, chain);
} else {
// Respond 401 Authorization Required.
}
}
doFilter(req, res, chain);
}
All servlet containers have a standard way to configure filter chains.
Complete implementation based on maerics answer.
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import sun.misc.BASE64Decoder;
public class AuthenticationFilter implements Filter {
private static final String AUTHORIZATION_HEADER_NAME = "Authorization";
private static final String WWW_AUTHENTICATE_HEADER_NAME = "WWW-Authenticate";
private static final String WWW_AUTHENTICATE_HEADER_VALUE = "Basic realm=\"Default realm\"";
private static final String BASIC_AUTHENTICATION_REGEX = "Basic\\s";
private static final String EMPTY_STRING = "";
private static final String USERNAME_PASSWORD_SEPARATOR = ":";
private static final BASE64Decoder DECODER = new BASE64Decoder();
public void init(FilterConfig arg0) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) req;
HttpServletResponse httpRes = (HttpServletResponse) res;
String authHeader = httpReq.getHeader(AUTHORIZATION_HEADER_NAME);
if (authHeader == null) {
this.requestAuthentication(httpRes);
return;
}
authHeader = authHeader.replaceFirst(BASIC_AUTHENTICATION_REGEX, EMPTY_STRING);
authHeader = new String(DECODER.decodeBuffer(authHeader));
if (StringUtils.countMatches(authHeader, USERNAME_PASSWORD_SEPARATOR) != 1) {
this.requestAuthentication(httpRes);
return;
}
String[] creds = authHeader.split(USERNAME_PASSWORD_SEPARATOR);
String username = creds[0];
String password = creds[1];
//TODO: implement this method
if (!authenticatedUser(username, password)) {
this.requestAuthentication(httpRes);
return;
}
chain.doFilter(req, res);
}
private void requestAuthentication(HttpServletResponse httpRes) {
httpRes.setHeader(WWW_AUTHENTICATE_HEADER_NAME, WWW_AUTHENTICATE_HEADER_VALUE);
httpRes.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
public void destroy() {
}
}
Related
I have a Spring Boot Web Application (Spring boot version 2.0.3.RELEASE) and running in an Apache Tomcat 8.5.5 server.
With the recent security policy which has imposed by Google Chrome (Rolled out since 80.0), it is requested to apply the new SameSite attribute to make the Cross-site cookie access in a more secure way instead of the CSRF. As I have done nothing related that and Chrome has set default value SameSite=Lax for the first-party cookies, one of my third-party service integration is failing due to the reason that chrome is restricting access of cross-site cookies when SameSite=Lax and if the third party response is coming from a POST request (Once the procedure completes third-party service redirect to our site with a POST request). in there Tomcat unable to find the session so it appends a new JSESSIONID (with a new session and the previous session was killed) at the end of the URL. So Spring rejects the URL as it contains a semicolon which was introduced by the new JSESSIONID append.
So I need to change the JSESSIONID cookie attributes(SameSite=None; Secure) and tried it in several ways including WebFilters.I have seen the same question and answers in Stackoverflow and tried most of them but ended up in nowhere.
can someone come up with a solution to change those headers in Spring Boot, please?
UPDATE on 06/07/2021 - Added correct Path attribute with new sameSite attributes to avoid session cookie duplication with GenericFilterBean approach.
I was able to come up with my own solution for this.
I have two kinds of applications which run on Spring boot which has different Spring security configurations and they needed different solutions to fix this.
CASE 1: No user authentication
Solution 1
In here you might have created an endpoint for the 3rd party response, in your application. You are safe until you access httpSession in a controller method. If you are accessing session in different controller method then send a temporary redirect request to there like follows.
#Controller
public class ThirdPartyResponseController{
#RequestMapping(value=3rd_party_response_URL, method=RequestMethod.POST)
public void thirdPartyresponse(HttpServletRequest request, HttpServletResponse httpServletResponse){
// your logic
// and you can set any data as an session attribute which you want to access over the 2nd controller
request.getSession().setAttribute(<data>)
try {
httpServletResponse.sendRedirect(<redirect_URL>);
} catch (IOException e) {
// handle error
}
}
#RequestMapping(value=redirect_URL, method=RequestMethod.GET)
public String thirdPartyresponse(HttpServletRequest request, HttpServletResponse httpServletResponse, Model model, RedirectAttributes redirectAttributes, HttpSession session){
// your logic
return <to_view>;
}
}
Still, you need to allow the 3rd_party_response_url in your security configuration.
Solution 2
You can try the same GenericFilterBean approach described below.
Case 2: Users need to be authenticated/sign in
In a Spring Web application where you have configured most of your security rules either through HttpSecurity or WebSecurity, check this solution.
Sample security config which I have tested the solution:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
......
..antMatchers(<3rd_party_response_URL>).permitAll();
.....
..csrf().ignoringAntMatchers(<3rd_party_response_URL>);
}
}
The Important points which I want to highlight in this configuration are you should allow the 3rd party response URL from Spring Security and CSRF protection(if it's enabled).
Then we need to create a HttpServletRequest Filter by extending GenericFilterBean class (Filter class did not work for me) and setting the SameSite Attributes to the JSESSIONID cookie by intercepting each HttpServletRequest and setting the response headers.
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class SessionCookieFilter extends GenericFilterBean {
private final List<String> PATHS_TO_IGNORE_SETTING_SAMESITE = Arrays.asList("resources", <add other paths you want to exclude>);
private final String SESSION_COOKIE_NAME = "JSESSIONID";
private final String SESSION_PATH_ATTRIBUTE = ";Path=";
private final String ROOT_CONTEXT = "/";
private final String SAME_SITE_ATTRIBUTE_VALUES = ";HttpOnly;Secure;SameSite=None";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String requestUrl = req.getRequestURL().toString();
boolean isResourceRequest = requestUrl != null ? StringUtils.isNoneBlank(PATHS_TO_IGNORE_SETTING_SAMESITE.stream().filter(s -> requestUrl.contains(s)).findFirst().orElse(null)) : null;
if (!isResourceRequest) {
Cookie[] cookies = ((HttpServletRequest) request).getCookies();
if (cookies != null && cookies.length > 0) {
List<Cookie> cookieList = Arrays.asList(cookies);
Cookie sessionCookie = cookieList.stream().filter(cookie -> SESSION_COOKIE_NAME.equals(cookie.getName())).findFirst().orElse(null);
if (sessionCookie != null) {
String contextPath = request.getServletContext() != null && StringUtils.isNotBlank(request.getServletContext().getContextPath()) ? request.getServletContext().getContextPath() : ROOT_CONTEXT;
resp.setHeader(HttpHeaders.SET_COOKIE, sessionCookie.getName() + "=" + sessionCookie.getValue() + SESSION_PATH_ATTRIBUTE + contextPath + SAME_SITE_ATTRIBUTE_VALUES);
}
}
}
chain.doFilter(request, response);
}
}
Then add this filter to the Spring Security filter chain by
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
....
.addFilterAfter(new SessionCookieFilter(), BasicAuthenticationFilter.class);
}
in order to determine where you need to place the new filter in Spring’s security filter chain, you can debug the Spring security filter chain easily and identify a proper location in the filter chain. Apart from the BasicAuthenticationFilter, after the SecurityContextPersistanceFilter would be an another ideal place.
This SameSite cookie attribute will not support some old browser versions and in that case, check the browser and avoid setting SameSite in incompatible clients.
private static final String _I_PHONE_IOS_12 = "iPhone OS 12_";
private static final String _I_PAD_IOS_12 = "iPad; CPU OS 12_";
private static final String _MAC_OS_10_14 = " OS X 10_14_";
private static final String _VERSION = "Version/";
private static final String _SAFARI = "Safari";
private static final String _EMBED_SAFARI = "(KHTML, like Gecko)";
private static final String _CHROME = "Chrome/";
private static final String _CHROMIUM = "Chromium/";
private static final String _UC_BROWSER = "UCBrowser/";
private static final String _ANDROID = "Android";
/*
* checks SameSite=None;Secure incompatible Browsers
* https://www.chromium.org/updates/same-site/incompatible-clients
*/
public static boolean isSameSiteInCompatibleClient(HttpServletRequest request) {
String userAgent = request.getHeader("user-agent");
if (StringUtils.isNotBlank(userAgent)) {
boolean isIos12 = isIos12(userAgent), isMacOs1014 = isMacOs1014(userAgent), isChromeChromium51To66 = isChromeChromium51To66(userAgent), isUcBrowser = isUcBrowser(userAgent);
//TODO : Added for testing purpose. remove before Prod release.
LOG.info("*********************************************************************************");
LOG.info("is iOS 12 = {}, is MacOs 10.14 = {}, is Chrome 51-66 = {}, is Android UC Browser = {}", isIos12, isMacOs1014, isChromeChromium51To66, isUcBrowser);
LOG.info("*********************************************************************************");
return isIos12 || isMacOs1014 || isChromeChromium51To66 || isUcBrowser;
}
return false;
}
private static boolean isIos12(String userAgent) {
return StringUtils.contains(userAgent, _I_PHONE_IOS_12) || StringUtils.contains(userAgent, _I_PAD_IOS_12);
}
private static boolean isMacOs1014(String userAgent) {
return StringUtils.contains(userAgent, _MAC_OS_10_14)
&& ((StringUtils.contains(userAgent, _VERSION) && StringUtils.contains(userAgent, _SAFARI)) //Safari on MacOS 10.14
|| StringUtils.contains(userAgent, _EMBED_SAFARI)); // Embedded browser on MacOS 10.14
}
private static boolean isChromeChromium51To66(String userAgent) {
boolean isChrome = StringUtils.contains(userAgent, _CHROME), isChromium = StringUtils.contains(userAgent, _CHROMIUM);
if (isChrome || isChromium) {
int version = isChrome ? Integer.valueOf(StringUtils.substringAfter(userAgent, _CHROME).substring(0, 2))
: Integer.valueOf(StringUtils.substringAfter(userAgent, _CHROMIUM).substring(0, 2));
return ((version >= 51) && (version <= 66)); //Chrome or Chromium V51-66
}
return false;
}
private static boolean isUcBrowser(String userAgent) {
if (StringUtils.contains(userAgent, _UC_BROWSER) && StringUtils.contains(userAgent, _ANDROID)) {
String[] version = StringUtils.splitByWholeSeparator(StringUtils.substringAfter(userAgent, _UC_BROWSER).substring(0, 7), ".");
int major = Integer.valueOf(version[0]), minor = Integer.valueOf(version[1]), build = Integer.valueOf(version[2]);
return ((major != 0) && ((major < 12) || (major == 12 && (minor < 13)) || (major == 12 && minor == 13 && (build < 2)))); //UC browser below v12.13.2 in android
}
return false;
}
Add above check in SessionCookieFilter like follows,
if (!isResourceRequest && !UserAgentUtils.isSameSiteInCompatibleClient(req)) {
This filter won't work in localhost environments as it requires a Secured(HTTPS) connection to set Secure cookie attribute.
For a detailed explanation read this blog post.
I was in same situation earlier. Since there is nothing like SameSite in javax.servlet.http.Cookie class so it's not possible to add that.
Part 1: So what I did is wrote a filter which intercepts the required third party request only.
public class CustomFilter implements Filter {
private static final String THIRD_PARTY_URI = "/third/party/uri";
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if(THIRD_PARTY_URI.equals(request.getRequestURI())) {
chain.doFilter(request, new CustomHttpServletResponseWrapper(response));
} else {
chain.doFilter(request, response);
}
}
enter code here
// ... init destroy methods here
}
Part 2: Cookies are sent as Set-Cookie response header. So this CustomHttpServletResponseWrapper overrides the addCookie method and check, if it is the required cookie (JSESSIONID), instead of adding it to cookie, it adds directly to response header Set-Cookie with SameSite=None attribute.
public class CustomHttpServletResponseWrapper extends HttpServletResponseWrapper {
public CustomHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void addCookie(Cookie cookie) {
if ("JSESSIONID".equals(cookie.getName())) {
super.addHeader("Set-Cookie", getCookieValue(cookie));
} else {
super.addCookie(cookie);
}
}
private String getCookieValue(Cookie cookie) {
StringBuilder builder = new StringBuilder();
builder.append(cookie.getName()).append('=').append(cookie.getValue());
builder.append(";Path=").append(cookie.getPath());
if (cookie.isHttpOnly()) {
builder.append(";HttpOnly");
}
if (cookie.getSecure()) {
builder.append(";Secure");
}
// here you can append other attributes like domain / max-age etc.
builder.append(";SameSite=None");
return builder.toString();
}
}
As mentioned in this answer:
Same-Site flag for session cookie in Spring Security
#Configuration
public static class WebConfig implements WebMvcConfigurer {
#Bean
public TomcatContextCustomizer sameSiteCookiesConfig() {
return context -> {
final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
context.setCookieProcessor(cookieProcessor);
};
}
}
but this seems even simpler
#Configuration
public static class WebConfig implements WebMvcConfigurer {
#Bean
public CookieSameSiteSupplier cookieSameSiteSupplier(){
return CookieSameSiteSupplier.ofNone();
}
}
Or ... even simpler, spring boot since 2.6.0 supports setting it in application.properties.
Spring documentation about SameSite Cookies
server.servlet.session.cookie.same-site = none
I want to create "pre" function. and in this function to check the session,
When some function in controller is called, I want that my "pre" function will called before it. and from the "pre" function I will pass the user to logIn page or to do the function.
something like this pseudo code:
if(!session)
return "redirect:login";
else
//calling to the selected function,
I saw some solutions to create this function, but the solution was to create it by: #ModelAttribute. and the problem is that with #ModelAttribute I didn't find any way to pass to another function in my controller.
More than, the selected function is always called after my #ModelAttribute finish,
How can I do that? there is a way to do something like this?
You can achieve that by using a servlet Filter. Here is a code snippet:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class RestrictionFilter implements Filter {
private static final String ACCES_PUBLIC = "/loginPage.jsp";
private static final String ATT_SESSION_USER = "user";
public void init( FilterConfig config ) throws ServletException {
}
public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain ) throws IOException,
ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
final HttpSession session = request.getSession();
/**
* check if user is not connected.
*/
if (session.getAttribute( ATT_SESSION_USER ) == null) {
/* Redirection to login page */
response.sendRedirect( request.getContextPath() + ACCES_PUBLIC );
} else {
/** access granted for the user*/
chain.doFilter( request, response );
}
}
public void destroy() {
}
}
Then add the filter to your web.xml like below:
<filter>
<filter-name>RestrictionFilter</filter-name>
<filter-class>yourPackage.RestrictionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RestrictionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I want to add a client name parameter to my URL, so when i receive a link like :
http://localhost:8080/client1 i want to test if my client is present in database, if so i want that the client receive the content of my site like if he call :
http://localhost:8080/
I tried to do this with a wrapper like indicated here :
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ChangeURIPathFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(ChangeURIPathFilter.class);
RequestWrapper modifiedRequest = null;
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String contextPath = ((HttpServletRequest) request).getContextPath();
String requestURI = httpRequest.getRequestURI();
modifiedRequest = new RequestWrapper(httpRequest, "/");
chain.doFilter(modifiedRequest, response);
}
class RequestWrapper extends HttpServletRequestWrapper {
private String originalValue;
public RequestWrapper(HttpServletRequest request) {
super(request);
}
#Override
public String getRequestURI() {
String originalURI = super.getRequestURI();
String s = super.getRequestURI();
if (StringUtils.equals(s, originalValue)) return originalValue
.replaceAll("client1", "");
else return s;
}
public RequestWrapper(HttpServletRequest request, String newValue) {
super(request);
this.originalValue = request.getRequestURI();
}
}
}
But all my tests failed. Can any one help me to resolve this ?
Thanks
I've got a project which is divided in different modules, you have for example a site and a forum.
The forum can be found at:
http://example.com/[forum]/
and for example can be:
http://example.com/support/
http://example.com/helpme/
http://example.com/aforum/
The site can be found at:
http://example.com/[site]/
and for example can be:
http://example.com/site1/
http://example.com/nice/
http://example.com/something/
The [forum] and [site] part are variable. In my database i lookup that "nice" is a site, "helpme" is a forum.
I have a spring RequestMapping for my ForumController:
#RequestMapping(value = { "/{simpleTitle:[0-9a-z-]+}" }, method = RequestMethod.GET, produces = "text/html")
public void list(#PathVariable String simpleTitle, Model model, HttpServletRequest req, HttpServletResponse resp) {
I have the samething for sites, so a SiteController:
#RequestMapping(value = { "/{simpleTitle:[0-9a-z-]+}" }, method = RequestMethod.GET, produces = "text/html")
public void list(#PathVariable String simpleTitle, Model model, HttpServletRequest req, HttpServletResponse resp) {
This of course goes bad, cause 2 controllers with the same requestmapping isn't good.
I can create a FrontController which with above request mapping, lookup what simpleTitle is (a forum or a site) and call functions to display a forum or a site. That works.
But it isn't very "spring" like and structured.
Is it possible "intercept" a request and internally forward (or call the function) on the controller myself?
This way i could have Interceptor which looks a simpleTitle, decides whether it is a forum or site, and "forwards"/"calls" the right controller.
Frankly I like #Luiggi Mendoza solution, but if you want an alternative, use something like this:
package eu.europa.acer.aris.ceremp.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
#Component(value = "yourCustomFilter")
public class YourCustomFilter extends OncePerRequestFilter{
private static final Logger logger = LoggerFactory.getLogger(YourCustomFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//String[] path = request.getRequestURL().toString().split("/");
if (letPagePass(request.getRequestURL().toString()) == false)
{
// if request is bound to static resource like js//img do nothing, the filter chain will activate
if (letResourcePass(request.getRequestURL().toString()))
{
}
else
{
String[] urlInfos = obtainUrlAndParametersLast(request.getRequestURL().toString());
// last element will always give back last part including any parameter
// first element will always be a controller modifier
response.sendRedirect(request.getContextPath()+rebuildControllerPath(urlInfos));
return;
}
}
filterChain.doFilter(request, response);
}
private String rebuildControllerPath(String[] pathElement )
{
//do your thing here
if ("forum".equals(pathElement[0]))
{
String addenda = "/forumController/";
for (String singlePart: pathElement)
{
addenda = addenda+singlePart+"/";
}
return addenda;
}
}
// bind forceful redirect
public boolean letPagePass(String url)
{
// if you have some page that are naturally unique among controllers that you want always to process natively
String[] path = url.split("/");
if (path[path.length-2].startsWith("yourCertainUrlIgnoringParameters"))
{
return true;
}
// directcall
else if (path[path.length-2].startsWith("yourCertainUrlIgnoringParameters2"))
{
return true;
}
else
{
return false;
}
}
public boolean letResourcePass(String url)
{
String[] path = url.split("/");
/*
this assumes you have always a strict structure so your image//js//reource will always be in
https://domainname/a/lot/of/folder/img/actuaresource.png
or
https://domainname/a/lot/of/folder/js/actuaresource.js
etc
*/
//image pass
if (path[path.length-2].equals("img") || url.contains("/img/"))
{
return true;
}
//css pass
else if (path[path.length-2].equals("css") || url.contains("/css/"))
{
return true;
}
//js pass
else if (path[path.length-2].equals("js") || url.contains("/js/"))
{
return true;
}
else
{
return false;
}
}
#Override
public void destroy() {
}
}
and add to your web.xml file the following xml snippet
<!-- your fi Filter -->
<filter>
<filter-name>yourCustomFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>yourCustomFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Cheers Luiggi Mendoza and witchedwiz for the great ideas!
While i was eating i came up with a very simple solution: use servlet filter. Add a request header with a HttpServletRequestWrapper (type: forum or type: site) inside the doFilter.
In the #RequestMapping add a headers property like headers = "type=forum".
Now #RequestMapping can have the same urls, they differ in request header. And all spring functionality works, although i only made a simple test case so far.
WebApplicationContextUtils helped to get spring beans in the servlet filter.
I'm developing a custom AuthHandler for our company.
The idea is to allow access based on user and service.
But i can't find a way to access the RegistredService.
Is there a way to pass the RegistredService to my AuthHandler ?
/**
* Mbox Auth Handler
*/
package lu.ion.cas.adaptors.mbox;
import org.jasig.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import lu.ion.cas.MboxAuthHelper;
import javax.validation.constraints.NotNull;
public class AuthHandler
extends AbstractPreAndPostProcessingAuthenticationHandler {
private MboxAuthHelper mboxAuthHelper;
private RequestContext context;
protected boolean doAuthentication(final Credentials credentials)
throws AuthenticationException {
return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);
}
protected boolean authenticateUsernamePasswordInternal(
final UsernamePasswordCredentials credentials)
throws AuthenticationException {
return mboxAuthHelper.load(credentials.getUsername(), credentials.getPassword(), "/auth/check") != null;
}
public boolean supports(Credentials credentials) {
return true;
}
public final void setMboxAuthHelper(final MboxAuthHelper mboxAuthHelper) {
this.mboxAuthHelper = mboxAuthHelper;
}
}
I'm using CAS 3.5.2.
I have used CAS for a few years and found that there are many ways to do everything. I don't know how (or if) you can pass the RegisteredService to your AuthHandler. I solved the same problem by using a custom AuthenticationFilter.
(backend)
Create AuthenticationFilter.java in/near your CAS project like this:
public class AuthenticationFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession();
String loginName = request.getRemoteUser();
String contextPath = request.getContextPath();
System.err.println("loginName is: " + loginName);
System.err.println("contextPath is: " + contextPath);
boolean isAuthorized = false;
// do work/query to find out if they are authorized
if (isAuthorized) {
chain.doFilter(request, response);
} else {
session.invalidate();
// print error page
}
}
#Override
public void init(FilterConfig config) throws ServletException {
}
#Override
public void destroy() {
}
}
(frontend) Then add to your filter chain. If you have a web.xml with existing CAS filters, it is easy.
...
<filter>
<filter-name>Custom Filter</filter-name>
<filter-class>
com.yoursite.filter.AuthenticationFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>Custom Filter</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
...
No there is no way to do this. If you want to implement authorization rules for CAS 3.5.2, you should review cas-addons:
https://github.com/Unicon/cas-addons/wiki