Cant get authentication filter working with embedded jetty - authentication

I have a REST handler servlet defined as follows (this works perfectly):
//REST handler context
ServletContextHandler restHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
restHandler.setContextPath("/");
// Jersey REST handling servlet
ServletHolder jerseyServlet = restHandler.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tell Jersey which REST service class to load....
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames", RestHandler.class.getCanonicalName());
I now want to add a authentication filter, which I do as:
FilterHolder authFilter = restHandler.addFilter(AuthFilter.class, "/",
EnumSet.of( DispatcherType.ASYNC,
DispatcherType.ERROR,
DispatcherType.FORWARD,
DispatcherType.INCLUDE,
DispatcherType.REQUEST));
if (authFilter == null) {
dlog.debug("Failed to load authentication filter");
};
All good so far, however, the filter does not fire on incoming REST. Calls still go through. The AuthFilter is straight from sample code:
public class AuthFilter implements javax.servlet.Filter {
private static final Logger dlog = Dlog.get();
public static final String AUTHENTICATION_HEADER = "Authorization";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter)
throws IOException, ServletException {
dlog.entry(request, response, filter);
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest.getHeader(AUTHENTICATION_HEADER);
AuthService authenticationService = new AuthService();
boolean authenticationStatus = authenticationService.authenticate(authCredentials);
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
dlog.exit();
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
I use handler collection as I also have a resource handler to serve static web pages besides the REST calls.
HandlerCollection handlerList = new HandlerCollection();
handlerList.setHandlers(new Handler[] { resourceHandler,
restHandler,
new DefaultHandler(),
requestLogHandler });
What else I need to do? I have scanned through number of related posts and come up empty. Thanks in advance.

Related

Browser not prompting for credentials using basic authentication

My goal is to provide authentication to a single resource on the server, for this I am using custom filter. I am not using #NameBinding because of constraint of using JAVA 1.6.Using Response.header(HttpHeaders.WWW_AUTHENTICATE,"Basic") is not prompting for credentials.
Using ContainerRequestFilter is not helping my cause as it will put filter on every resource of server.
Filter
#Provider
public class AuthenticationFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
System.out.println("Entered authentication filter");
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.AUTHORIZATION,"Basic")
.entity("Credentials are required to access this resource.")
.build());
// chain.doFilter(req, resp);
}
#Override
public void init(FilterConfig arg0) throws ServletException {}
#Override
public void destroy() {}
}
web.xml mapping
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>Utils.LDAPAuthentication.AuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/download</url-pattern>
</filter-mapping>
The response I am getting on hitting the webservice is
So as suggested by Paul , I used HttpServletResponse.
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if(request.getHeader("Authorization")==null){
response.setHeader(HttpHeaders.WWW_AUTHENTICATE,"Basic");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
else{
String credentials = request.getHeader("Authorization");
}

Spring Security authorize in REST (or form-login) app by HTTP GET

I'm writing backend app based on Spring Boot without any views (templates), because client app will use it's own HTML.
I'm trying change default behavior (HTTP POST) Spring Security form-login authentication - use HTTP GET and POST. Yes, I know, it's bad for security, but it's requirement.
How I can do it?
My app:
Application
package net.company.rest;
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SecurityConfig
package net.company.rest.config;
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthSuccessHandler authSuccessHandler;
#Autowired
private AuthFailureHandler authFailureHandler;
#Autowired
private AuthEntryPoint authEntryPoint;
// configure security
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
http.exceptionHandling().authenticationEntryPoint(authEntryPoint);
http.formLogin().usernameParameter("user").passwordParameter("pass");
http.formLogin().successHandler(authSuccessHandler).failureHandler(authFailureHandler);
http.logout().permitAll();
http.cors();
http.csrf().disable();
}
// enable security
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
for (int i = 1; i <= 10; i++) {
auth.inMemoryAuthentication().withUser("user" + i).password("user" + i).roles("USER");
}
}
}
AuthEntryPoint
package net.company.rest.component;
#Component
public class AuthEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest req,
HttpServletResponse resp,
AuthenticationException ex) throws IOException, ServletException {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
UniversalMessage msg = new UniversalMessage(1, "not authenticated");
try {
resp.getWriter().print(new ObjectMapper().writeValueAsString(msg));
} catch (JsonProcessingException e) {
resp.getWriter().print(e.toString());
}
resp.getWriter().flush();
}
}
AuthSuccessHandler
package net.company.rest.component;
#Component
public class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest req,
HttpServletResponse resp,
Authentication auth) throws ServletException, IOException {
resp.setStatus(HttpServletResponse.SC_OK);
UniversalMessage msg = new UniversalMessage(0, "auth success");
try {
resp.getWriter().print(new ObjectMapper().writeValueAsString(msg));
} catch (JsonProcessingException e) {
resp.getWriter().print(e.toString());
}
resp.getWriter().flush();
clearAuthenticationAttributes(req);
}
}
AuthFailureHandler
package net.company.rest.component;
#Component
public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest req,
HttpServletResponse resp,
AuthenticationException ex) throws IOException, ServletException {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
UniversalMessage msg = new UniversalMessage(1, "auth error");
try {
resp.getWriter().print(new ObjectMapper().writeValueAsString(msg));
} catch (JsonProcessingException e) {
resp.getWriter().print(e.toString());
}
resp.getWriter().flush();
}
}
The problem was that above code didn't solve problem, login by HTTP GET didn't work anyway.
There is steps for solve:
Add new class extends UsernamePasswordAuthenticationFilter
package net.company.rest.config.web;
#Slf4j
public class AuthByGetFilter extends UsernamePasswordAuthenticationFilter {
public AuthByGetFilter() {
super();
// change auth parameters
setUsernameParameter("user");
setPasswordParameter("pass");
// allow GET
setPostOnly(false);
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "GET"));
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
log.debug("Method: {}, Request: {}, params was hidden for security", request.getMethod(), request.getRequestURL());
log.debug("Authentication request failed: {}", failed.toString());
log.debug("Updated SecurityContextHolder to contain null Authentication");
log.debug("Delegating to authentication failure handler " + getFailureHandler());
getRememberMeServices().loginFail(request, response);
getFailureHandler().onAuthenticationFailure(request, response, failed);
}
}
Use this class as filter in the above described SecurityConfig class
...
// configure security
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(authByGetFilter(), UsernamePasswordAuthenticationFilter.class); // add authByGetFilter
...
}
// allow authentication by http GET method
#Bean
public AuthByGetFilter authByGetFilter() throws Exception {
AuthByGetFilter authByGetFilter = new AuthByGetFilter();
authByGetFilter.setAuthenticationManager(authenticationManagerBean());
authByGetFilter.setAuthenticationFailureHandler(authFailureHandler);
authByGetFilter.setAuthenticationSuccessHandler(authSuccessHandler);
return authByGetFilter;
}
...

Why ErrorHandler component is not registering properly in SlingMainServlet?

I have a simple component for my own ErrorHandler implementation:
#Component(immediate = true)
#Service
#Properties(
#Property(name = "service.ranking", intValue = 1)
)
public class MyErrorHandler implements ErrorHandler {
#Override
public void handleError(int status, String message, SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
handleError(status, request, response);
}
#Override
public void handleError(Throwable throwable, SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
handleError(505, request, response);
}
private void handleError(int status, SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
if (status != 200) {
try {
request.getRequestDispatcher("/etc/errors/" + status + ".html").forward(request, response);
} catch (ServletException e) {
throw new IllegalStateException("No error pages");
}
}
}
If I deploy the component to Sling, nothings happen until restart of whole Sling. It's not a good solution I guess.
Short explanation about using ErrorHandler instead of handling scripts (for those who would ask 'why you didn't use overloaded script)
Writing own Servlet is too complex for that easy behaviour
Scripts are less maintainable than java code (in sling).

Apache HttpClient Official Example - Releasing Resources method

In this official example for Apache HttpClient, there's no mention of releasing request or response objects. Are they released as part of httpclient.close() or releaseResources method needs to be overridden with something?
final CountDownLatch latch2 = new CountDownLatch(1);
final HttpGet request3 = new HttpGet("http://www.apache.org/");
HttpAsyncRequestProducer producer3 = HttpAsyncMethods.create(request3);
AsyncCharConsumer<HttpResponse> consumer3 = new AsyncCharConsumer<HttpResponse>() {
HttpResponse response;
#Override
protected void onResponseReceived(final HttpResponse response) {
this.response = response;
}
#Override
protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
// Do something useful
}
#Override
protected void releaseResources() {
}
#Override
protected HttpResponse buildResult(final HttpContext context) {
return this.response;
}
};
httpclient.execute(producer3, consumer3, new FutureCallback<HttpResponse>() {
public void completed(final HttpResponse response3) {
latch2.countDown();
System.out.println(request2.getRequestLine() + "->" + response3.getStatusLine());
}
public void failed(final Exception ex) {
latch2.countDown();
System.out.println(request2.getRequestLine() + "->" + ex);
}
public void cancelled() {
latch2.countDown();
System.out.println(request2.getRequestLine() + " cancelled");
}
});
latch2.await();
} finally {
httpclient.close();
}
One needs to override #releaseResources() only if the consumer makes use of system resources such as files, pipes, etc. If response content is always held in memory it gets GCed the normal way.

Authentication filter and servlet for login

I've a filter used for the login. It performs a textual checking, on fields "Username" and "Password". If and only if the textual checking is correctly done the request goes to the Servlet. This latter performs the control that has to interact with the Database. Is this chain correct?
Preface: I gather you're using homegrown login instead of container managed login. For all ways, see How to handle authentication/authorization with users in a database?
The filter (the interceptor) shouldn't check the validity of the username/password combo. That's the responsibility of the servlet (the controller).
The filter should merely check if the user is logged-in or not (usually by just checking the presence of a session attribute) and then continue the request or block it by redirecting back to the login page.
#WebFilter("/*")
public class LoginFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String loginURI = request.getContextPath() + "/login";
boolean loggedIn = session != null && session.getAttribute("user") != null;
boolean loginRequest = request.getRequestURI().equals(loginURI);
if (loggedIn || loginRequest) {
chain.doFilter(request, response);
} else {
response.sendRedirect(loginURI);
}
}
// ...
}
The servlet should collect the submitted data, find the associated User in database and if found then store it as a session attribute and then redirect to the home page, else redisplay the form with validation errors.
#WebServlet("/login")
public class LoginServlet extends HttpServlet {
#EJB
private UserService userService;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Map<String, String> messages = new HashMap<String, String>();
if (username == null || username.isEmpty()) {
messages.put("username", "Please enter username");
}
if (password == null || password.isEmpty()) {
messages.put("password", "Please enter password");
}
if (messages.isEmpty()) {
User user = userService.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user);
response.sendRedirect(request.getContextPath() + "/home");
return;
} else {
messages.put("login", "Unknown login, please try again");
}
}
request.setAttribute("messages", messages);
request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
}
}
See also:
Our servlet-filters wiki page
Our servlets wiki page