I use a filter to check URL patterns for the logged in user.
But I have many URL patterns I need to filter.
{ "/table/*", "/user/*", "/contact/*", "/run/*", "/conf/*", ..., ..., ...}
It's becoming unmaintainable. It will be simpler just to exclude:
{ "/", "/login", "/logout", "/register" }
How can I achieve this?
#WebFilter(urlPatterns = { "/table/*","/user/*", "/contact/*","/run/*","/conf/*"})
public class SessionTimeoutRedirect implements Filter {
protected final Logger logger = LoggerFactory.getLogger("SessionFilter");
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (request.getSession().getAttribute("Id") != null) {
chain.doFilter(req, res);
} else {
logger.debug("session is null:"+request.getRequestURL());
response.sendRedirect(request.getContextPath()+"/login");
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
#Override
public void destroy() {
}
}
The servlet API doesn't support an "exclude" URL pattern.
Your best bet is to just map on /* and compare the HttpServletRequest#getRequestURI() against the set of allowed paths.
#WebFilter("/*")
public class LoginFilter implements Filter {
private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("", "/login", "/logout", "/register")));
#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(false);
String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", "");
boolean loggedIn = (session != null && session.getAttribute("Id") != null);
boolean allowedPath = ALLOWED_PATHS.contains(path);
if (loggedIn || allowedPath) {
chain.doFilter(req, res);
}
else {
response.sendRedirect(request.getContextPath() + "/login");
}
}
// ...
}
You can use initParam to have some excluded patterns and implement your logic. This is basically the same as BalusC's answer except by using initParam it can be written in the web.xml if you want/need to.
Below I am ignoring some binary (jpeg jpg png pdf) extensions:
#WebFilter(urlPatterns = { "/*" },
initParams = { #WebInitParam(name = "excludedExt", value = "jpeg jpg png pdf") }
)
public class GzipFilter implements Filter {
private static final Set<String> excluded;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
String excludedString = filterConfig.getInitParameter("excludedExt");
if (excludedString != null) {
excluded = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(excludedString.split(" ", 0))));
} else {
excluded = Collections.<String>emptySet();
}
}
boolean isExcluded(HttpServletRequest request) {
String path = request.getRequestURI();
String extension = path.substring(path.indexOf('.', path.lastIndexOf('/')) + 1).toLowerCase();
return excluded.contains(extension);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.print("GzipFilter");
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (isExcluded(httpRequest)) {
chain.doFilter(request, response);
return;
}
// Do your stuff here
}
}
Related
I am trying to write unit tests for the following Filter class, but I am not sure how it can be done. I am working on a Spring project and trying to fix a vulnerability, that is why I am using this class.
public class HSTSFilter implements Filter {
private static final String HEADER_NAME = "Strict-Transport-Security";
private static final String MAX_AGE_DIRECTIVE = "max-age=%s";
private static final String INCLUDE_SUB_DOMAINS_DIRECTIVE = "includeSubDomains";
private static final Logger logger = LoggerFactory.getLogger(HSTSFilter.class);
private int maxAgeSeconds = 0;
private boolean includeSubDomains = false;
private String directives;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.info("request.isSecure() :: {}" , request.isSecure());
if (request.isSecure() && response instanceof HttpServletResponse) {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader(HEADER_NAME, this.directives);
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
maxAgeSeconds = Integer.parseInt(filterConfig.getInitParameter("maxAgeSeconds"));
includeSubDomains = "true".equals(filterConfig.getInitParameter("includeSubDomains"));
if (this.maxAgeSeconds <= 0) {
throw new ServletException("Invalid maxAgeSeconds value :: " + maxAgeSeconds);
}
this.directives = String.format(MAX_AGE_DIRECTIVE, this.maxAgeSeconds);
if (this.includeSubDomains) {
this.directives += (" ; " + INCLUDE_SUB_DOMAINS_DIRECTIVE);
}
}
#Override
public void destroy() {
}
}
In your unit test, create a mock object of ServletRequest, ServletResponse, and FilterChain.
Call doFilter with these mock objects.
I am writing a Spring boot filter to verify request data before it hits the rest controller. To avoid HttpServletRequest read ServletInputStream twice, I writed a CustomRequestWrapper to wrap it. But When I send a post request(conetentType = "multipart/form-data") by postman , I get a null in rest controller.
Here is my filter:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
CustomReqeustWrapper requestWrapper = new CustomReqeustWrapper(req);
CustomResponseWrapper responseWrapper = new CustomResponseWrapper(resp);
// get requestBody from requestWrapper and verify.
readContent(requestWrapper);
chain.doFilter(requestWrapper, responseWrapper);
writeContent(response);
}
Here is my RequestWrapper:
public class CustomRequestWrapper extends HttpServletRequestWrapper {
private byte[] requestBody = new byte[0];
public CustomRequestWrapper (HttpServletRequest request) {
super(request);
try {
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
#Override
public int read() throws IOException {
return bais.read();
}
#Override
public boolean isFinished() {
return false;
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener listener) {
}
};
}
public byte[] getRequestData() {
return requestBody;
}
public void setRequestData(byte[] requestData) {
this.requestBody = requestData;
}
}
Here is my controller:
#PostMapping("/exchange")
public ResponseEntity<Void> keyExchange(HttpServletRequest request, String Key) throws Exception {
// get the "key" is null.
}
If the type of parameter 'key' is String,maybe you should't use 'multipart' data type.
For all I know,I had met this problem once.The wrapper is useless for file parameter.
If you have to use 'multipart' type,perhaps you should use other manner to figure out it.
I want to redirect http to https, a solution I found is using a filter to do so, I have implemented the filter which is used to redirect http protocol to https
but when I try to open the site, i get google error,
"Try:
Reloading the page
Clearing your cookies
ERR_TOO_MANY_REDIRECTS"
am not limited to using the filter, if any other approaches can solve the above, please mention them
public class HTTPSFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI();
String getProtocol = req.getScheme();
String getDomain = req.getServerName();
String getPort = Integer.toString(req.getServerPort());
if (getProtocol.toLowerCase().equals("http")) {
// Set response content type
response.setContentType("text/html");
// New location to be redirected
String httpsPath = "https" + "://" + getDomain + uri;
String site = new String(httpsPath);
res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
res.setHeader("Location", site);
}
// Pass request back down the filter chain
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
web.xml
<filter>
<filter-name>HTTPS</filter-name>
<filter-class>com.simsarak.filters.HTTPSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HTTPS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Modify the filter as below. You don't need to set the Status and Location header as response.sendRedirect() should take care of it.
public class HTTPSFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI();
String getProtocol = req.getScheme();
String getDomain = req.getServerName();
String getPort = Integer.toString(req.getServerPort());
if (getProtocol.toLowerCase().equals("http")) {
// New location to be redirected
String httpsPath = "https" + "://" + getDomain + uri;
//redirect
res.sendRedirect(httpsPath);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
I'm using Spring Security to protect my resources. I would like to have an expression like following:
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("BUSINESS");
//Example
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("REFUGEE").and().access(principal.id==#id);
My SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new TokenAuthenticationFilter(userService), AnonymousAuthenticationFilter.class);
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
//Options preflight
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
//ACL
http.authorizeRequests().antMatchers("/auth/**").anonymous();
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("BUSINESS");
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("REFUGEE").and();
http.authorizeRequests().antMatchers("/user/**").hasAnyRole("ADMIN", "REFUGEE", "BUSINESS");
}
}
And the TokenAuthenticationFilter:
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private UserService userService;
public TokenAuthenticationFilter(UserService userService) {
super("/");
this.userService = userService;
}
private final String HEADER_SECURITY_TOKEN = "Authorization";
private final String PARAMETER_SECURITY_TOKEN = "access_token";
private String token = "";
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
this.token = request.getHeader(HEADER_SECURITY_TOKEN);
if ("".equals(this.token) || this.token == null) {
this.token = request.getParameter(PARAMETER_SECURITY_TOKEN);
}
//Attempt to authenticate
Authentication authResult;
authResult = attemptAuthentication(request, response);
if (authResult == null) {
chain.doFilter(request, response);
} else {
successfulAuthentication(request, response, chain, authResult);
}
}
/**
* Attempt to authenticate request - basically just pass over to another
* method to authenticate request headers
*/
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication userAuthenticationToken = authUserByToken();
if (userAuthenticationToken == null) {
//throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
}
return userAuthenticationToken;
}
/**
* authenticate the user based on token, mobile app secret & user agent
*
* #return
*/
private Authentication authUserByToken() {
Authentication securityToken = null;
try {
// TODO - just return null - always fail auth just to test spring setup ok
User user = userService.findUserByAccessToken(this.token);
securityToken = new PreAuthenticatedAuthenticationToken(
user, null, user.getAuthorites());
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return securityToken;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
I want to do some log in my system, like user action,
and I know in the servelet I can get the request with all the session,parameter..etc
So I want to write the Servlet
public class UserActionCheck extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
Map map = request.getParameterMap();
Set keSet = map.entrySet();
for (Iterator itr = keSet.iterator(); itr.hasNext(); ) {
Map.Entry me = (Map.Entry) itr.next();
Object ok = me.getKey();
Object ov = me.getValue();
String[] value = new String[1];
if (ov instanceof String[]) {
value = (String[]) ov;
} else {
value[0] = ov.toString();
}
for (int k = 0; k < value.length; k++) {
System.out.println(ok + "=" + value[k]);
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//HttpSession session = request.getSession();
}
}
then I can see the parameter output in the tomcat console..but I get the blank page..
It seems the page is stop after doGet method..
so how should I make it continue?
use that RequestDispatcher?
also how to handle in the doPost?
For your purpose, the best way would be to use a Filter.
Example :
#WebFilter(filterName = "monitoringFilter", urlPatterns = { "/*" })
public class MonitoringFilter implements Filter
{
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
{
// Right here do your stuff pretty much like in a servlet
request // ... get information you need
// Process request as normal
chain.doFilter(request,response);
}
#Override
public void init(FilterConfig config) throws ServletException
{
}
#Override
public void destroy()
{
}
}
More info :
Filter
You should use log4j and FileAppender to implement logging in your application.
Something like this :::
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
class A{
static Log log = LogFactory.getLog(A.class);
void methodA(){
try{
log.info("I am inside A");
} catch(Exception e) {
log.error("error" , e);
}
}
}