I need to create a gated content setup in aem. So basically, the page ( which contains some assets) should only be reachable after the user has submitted a form. And the assets should also be not accessible directly without submitting the form. So I have created a servlet filter to achieve this, the page logic works fine but when I access assets directly the request is not reaching the servlet filters. The below is my code please let me know if there is some mistake or there is any other preferred method, I have also tried setting this ServiceRanking(1)
#Component(service = Filter.class,
property = {
EngineConstants.SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST,
})
#ServiceRankin(-700)
public class GatedContentFilter implements Filter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain filterChain) throws IOException, ServletException {
final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
final SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;
final Resource resource = slingRequest.getResource();
if(resource.getPath().startsWith("/content/abc")) {
//page logic
}
else if (resource.getPath().startsWith("/content/dam/abc/gated-assets")) {
//assets logic
}
filterChain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
Related
I am using Spring security and I have a custom Authorization Filter without explicit URL mapping. I have added my Authorization filter after UsernamePasswordAuthenticationFilter.class.
The problem with this approach is, even for the URL patterns which are either permitted or permitted with authentication are going through my Authorization Filter. I do not want to explicitly configure my filter mapping on Authorization Filter since I feel that is redundant(already present in WebSecurityConfigurerAdapter). Is there anyway I can get access to Spring Security metadata or any other way to skip the Authorization Filter for the URLs which are marked as permitAll() or authenticated()?
public class AuthorizationFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
//Calls a webservice to fetch the authorities based on the URL pattern.
//Adds the authentication principal with Authorities to SecurityContextHolder.
}
public void init(FilterConfig config) throws ServletException {
}
}
I have solved this by extending WebExpressionVoter as below:
public class CustomAuthoritiesVoter extends WebExpressionVoter {
private static final String SUPPORTED_CLASS = "org.springframework.security.web.access.expression.WebExpressionConfigAttribute";
#Override
public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) {
String exp = getExpressionString(attributes);
if (null != exp && !StringUtils.equalsAny(exp, "authenticated", "permitAll")) {
//Call service to fetch the authorities based on the userID and URL
//Set Authentication principal along with fetched Authorities using SecurityContextHolder
}
return super.vote(authentication, fi, attributes);
}
private String getExpressionString(Collection<ConfigAttribute> attributes) {
try {
for (ConfigAttribute attribute : attributes) {
if (attribute.getClass().isAssignableFrom(Class.forName(SUPPORTED_CLASS))) {
return attribute.toString();
}
}
} catch (ClassNotFoundException e) {
log.warn(e.getMessage(), e);
}
return null;
}
}
My configuration of Spring Security is
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**"); // #3
}
Taken from here.
The documentation for ignorig says
Allows adding RequestMatcher instances that should that Spring Security should ignore. ... Typically the requests that are registered should be that of only static resources.
I would like to add some headers to files served from resources.
E.g.: Strict-Transport-Security: max-age=31536000, X-Content-Type-Options: nosniff.
How I can do it?
One solution it to change it to
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/resources/**").permitAll()
.and()
.antMatcher("/resources/**").headers().cacheControl()
}
Example how to allow cache control headers PLUS ALL DEFAULT SPRING SECURITY HEADERS.
I have struggled with the same problem. When I ignore specific requests in WebSecurity, the headers were gone.
I fixed the missing headers, by applying a filter on each request that adds my headers.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(securityHeaderFilter, BasicAuthenticationFilter.class)
...
}
The filter code looks like this. The important thing to note here, is that the Filter must be declared as a #Component. When you miss the #Component annotation, the filter will be ignored.
#Component
public class SecurityHeaderFilter implements Filter {
#Override
public void init(FilterConfig fc) throws ServletException {
// Do nothing
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader(
"custom-header1", "header-value1");
httpServletResponse.setHeader(
"custom-header2", "header-value2");
chain.doFilter(request, response);
}
#Override
public void destroy() {
// Do nothing
}
}
I have used the following solution:
#Bean
public FilterRegistrationBean setHeaders() {
HstsHeaderWriter hstsHeaderWriter = new HstsHeaderWriter(31536000, true);
XContentTypeOptionsHeaderWriter xContentTypeOptionsHeaderWriter = new XContentTypeOptionsHeaderWriter();
List<HeaderWriter> headerWriters = new ArrayList<>();
headerWriters.add(hstsHeaderWriter);
headerWriters.add(xContentTypeOptionsHeaderWriter);
HeaderWriterFilter headerWriterFilter = new HeaderWriterFilter(headerWriters);
FilterRegistrationBean bean = new FilterRegistrationBean(headerWriterFilter);
bean.setOrder(1);
return bean;
}
The above bean will add a filter globally on all the resources(even the ignoring ones). You can checkout the various implementations of org.springframework.security.web.header.HeaderWriter.java for the different kinds of security headers and add them all to HeaderWriterFilter.java.
Assume I have some servlets called /servlet1, /servlet2, etc in my code.
My goal is to serve two different applications with the same set of servlets:
/myapp1/servlet1 -> use /login with a parameter called myapp1
/myapp2/servlet2 -> use /login with a parameter called myapp2
I guess I have to use filter and filter mapping but I had no luck make it work so far.
here is an exemple of filter:
public class LoginFilter implements Filter {
public LoginFilter() {
// TODO Auto-generated constructor stub
}
public void destroy() {
// TODO Auto-generated method stub
}
public String clean(String s){
s=s.replaceAll("[^a-zA-Z0-9]", "");
s=s.trim();
return s;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest) request;
HttpServletResponse resp=(HttpServletResponse) response;
String user=req.getParameter("username");
String pass=req.getParameter("password");
req.removeAttribute("username");
req.removeAttribute("password");
user=clean(user);
pass=clean(pass);
req.setAttribute("username", user);
req.setAttribute("password", pass);
chain.doFilter(req, response);
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
and here is web.xml
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>pack.LoginFilter</filter-class> </filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/some-url-patter</url-pattern> </filter-mapping>
you have to have the same url pattern as the servlets too add the filter. you can have any number of filters.
it works something like this : filter1->filter2->filter3->...->servlet
I'm writing a small servlet to prevent spam requests from an J2ME app. But, i don't know how to do this.
Could you help me or suggest to me some links/posts about this?
I assume you have another Servlet that handles 'valid' requests and you want spam requests to be filtered out?
If that is so, then you need a Filter.
You would configure it in your web.xml (or by annotation) to be applied to all requests going to your actual Servlet and implement it like that:
public class SpamFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
// maybe read some configuration, e.g. rules that say what is spam and what is not
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (isValidRequest(request)) {
chain.doFilter(request, response);
} else {
// request is spam, prevent further processing (so, do nothing)
}
}
#Override
public void destroy() {}
}
AFAIK, the httpsessionlisterner implementation listener class is get instantiated when the first session is created.
Therefore, i would like access this instance because i need to count how many active session and display it some where and i would like to check which user is currently login. In the code below, there is list instance variable, i need to access this listener class in order to access the private variable.
#WebListener()
public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener {
private List<HttpSession> sessionList;
public SessionListener() {
sessionList = new ArrayList<HttpSession>();
}
#Override
public void sessionCreated(HttpSessionEvent se) {
sessionList.add(se.getSession());
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
sessionList.remove(se.getSession());
}
#Override
public void attributeAdded(HttpSessionBindingEvent event) {
}
#Override
public void attributeRemoved(HttpSessionBindingEvent event) {
}
#Override
public void attributeReplaced(HttpSessionBindingEvent event) {
}
/**
* #return the sessionList
*/
public List<HttpSession> getSessionList() {
return Collections.unmodifiableList(sessionList);
}
Please help.
Thanks.
I have to make a few assumptions as you don't say how your authentication method works.
I will assume that your username will be contained in your HttpServletRequest (this is very common). Unless you have specifically coded your session to contain the username it will not contain the username of the authenticated user - the username is usually confined to the HttpServletRequest. Therefore you will not usually achieve your goal by using an HttpSessionListener. You probably know this but there are various "scopes".
application scope (ServletContext) - per application
session scope (HttpSession) - per session
request scope (HttpServletRequest) - per request
As I said, the username is usually stored in the request scope. You can access the session and application scopes from the request scope. You cannot access the request scope from the session scope (as this doesn't make sense!).
To solve your problem I would create a Map stored in the application scope and use a ServletFilter to populate it. You might want to use a time based cache (using the session time-out value) rather than a straight map as mostly sessions are started but timeout rather than get explicitly terminated by the user. kitty-cache is a really simple time based cache that you could use for this purpose.
Anyway a code sketch (untested) might look something like this:
public class AuthSessionCounter implements Filter {
private static final String AUTHSESSIONS = "authsessions";
private static ServletContext sc;
public void init(FilterConfig filterConfig) throws ServletException {
sc = filterConfig.getServletContext();
HashMap<String, String> authsessions = new HashMap<String, String>();
sc.setAttribute(AUTHSESSIONS, authsessions);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest hsr = (HttpServletRequest) request;
if (hsr.getRemoteUser() != null) {
HttpSession session = hsr.getSession();
HashMap<String, String> authsessions = (HashMap<String, String>) sc.getAttribute(AUTHSESSIONS);
if (!authsessions.containsKey(session.getId())) {
authsessions.put(session.getId(), hsr.getRemoteUser());
sc.setAttribute(AUTHSESSIONS, authsessions);
}
}
chain.doFilter(request, response);
}
public void destroy(){}
}
You should now be able to obtain details of who and how many users are logged in from the authsessions Map that is stored in the application scope.
I hope this helps,
Mark
UPDATE
My authentication is works by checking
the username and password in servlet
and create a new session for it.
In which case a HttpSessionListener might work for you - although as I mentioned before you probably still need to use a time based cache due to the way that most user sessions timeout rather than terminate. My untested code sketch would now look something like this:
public class SessionCounter
implements HttpSessionListener, HttpSessionAttributeListener {
private static final String AUTHSESSIONS = "authsessions";
private static final String USERNAME = "username";
private static ServletContext sc;
public void sessionCreated(HttpSessionEvent se) {
if (sc == null) {
sc = se.getSession().getServletContext();
HashMap<String, String> authsessions = new HashMap<String, String>();
sc.setAttribute(AUTHSESSIONS, authsessions);
}
}
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
HashMap<String, String> authsessions =
(HashMap<String, String>) sc.getAttribute(AUTHSESSIONS);
authsessions.remove(session.getId());
sc.setAttribute(AUTHSESSIONS, authsessions);
}
public void attributeAdded(HttpSessionBindingEvent se) {
if (USERNAME.equals(se.getName())) {
HttpSession session = se.getSession();
HashMap<String, String> authsessions =
(HashMap<String, String>) sc.getAttribute(AUTHSESSIONS);
authsessions.put(session.getId(), (String) se.getValue());
sc.setAttribute(AUTHSESSIONS, authsessions);
}
}
public void attributeRemoved(HttpSessionBindingEvent se) {}
public void attributeReplaced(HttpSessionBindingEvent se) {}
}