Spring MVC AccessDeniedHandler multiple tries - java

I want to give incoming user a change to try at least couple of time before sending him to "denypage". By default in Spring MVC if user has wrong permissions and try to access "/admin" resource, Spring mvc redirects user to "denypage" like 403 default one. What I want to allow using some attempts to try before sending him to this page. I have tried the next code:
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyAccessDeniedHandler implements AccessDeniedHandler {
private static final int MAX_ATTEMPTS = 2;
private int counter = 0;
private String errorPage;
public MyAccessDeniedHandler() {}
public MyAccessDeniedHandler(String errorPage) {
this.errorPage = errorPage;
}
public String getErrorPage() {
return errorPage;
}
public void setErrorPage(String errorPage) {
this.errorPage = errorPage;
}
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException exception) throws IOException, ServletException {
counter =+ 1;
if(counter < MAX_ATTEMPTS) {
HttpSession session = request.getSession(false);
session.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
response.sendRedirect("fail2login");
} else {
response.sendRedirect(errorPage);
}
}
}
But this code redirects request to "fail2login" and when I try another attempt, I am redirected to default URL, not to initial one ("/admin" for example)((
Any propositions?

Related

Not able to override cookie

I am using Servlet Filter to intercept request which has cookies present and then trying to override specific cookie value using ThreadLocal.
But the overridden value is not getting reflected. Also, it is not able to add a new cookie when tried. Couldn't able to figure out what I am doing wrong.
Controller Class has two get endpoints. With cookie endpoint I am trying to add a cookie in the response, to intercept and test it when I hit override endpoint afterwards.
#RestController
public class FilterController {
#Autowired
AlphaCookie alphaCookie;
#Autowired
AlphaCookieFilter alphaCookieFilter;
#GetMapping("override") // testing endpoint for overriding cookie
public String cookieTest() {
this.alphaCookieFilter.persist(new AlphaCookie("newValue"));
return "Cookie Overriden, { AlphaCookie: newValue }";
}
#GetMapping("cookie") // to add cookie for testing
public String addCookieToBrowser(HttpServletResponse httpServletResponse) {
Cookie cookie = new Cookie("AlphaCookie", "oldValue");
cookie.setMaxAge(3600);
httpServletResponse.addCookie(cookie);
return "Cookie added, { AlphaCookie: old }";
}
}
Filter to intercept the request and check for the specific cookie, also override the cookie
#Component("alphaCookieFilter")
public class AlphaCookieFilter implements Filter {
#Autowired
private ApplicationContext applicationContext;
public static final ThreadLocal<HttpServletResponse> RESPONSE_HOLDER = new ThreadLocal<>();
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
RESPONSE_HOLDER.set((HttpServletResponse) response);
String activeCookie = null;
if (httpServletRequest.getCookies() != null) {
for (Cookie cookie: httpServletRequest.getCookies()) {
if ("AlphaCookie".equals(cookie.getName())) {
activeCookie = cookie.getValue();
}
}
}
this.applicationContext.getBean("alphaCookie", AlphaCookie.class)
.override(new AlphaCookie(activeCookie));
chain.doFilter(request, response);
RESPONSE_HOLDER.remove();
}
public void persist(AlphaCookie alphaCookie) {
Cookie cookie = new Cookie("AlphaCookie", alphaCookie.getActiveCookie());
cookie.setDomain("code.org");
cookie.setPath("/");
cookie.setMaxAge(-1);
cookie.setSecure(true);
cookie.setHttpOnly(true);
RESPONSE_HOLDER.get().addCookie(cookie);
}
}
POJO to store the cookie value
#Component("alphaCookie")
public class AlphaCookie implements Serializable {
private static final long serialVersionUID = 1L;
private String activeCookie;
public AlphaCookie() {
super();
}
public AlphaCookie(String activeCookie) {
this();
this.activeCookie = activeCookie;
}
public void override(AlphaCookie alphaCookie) {
synchronized (this) {
this.activeCookie = alphaCookie.activeCookie;
}
}
public String getActiveCookie() {
return this.activeCookie;
}
}
On debugging I found below properites are not allowing me to override the cookie value.
cookie.setDomain("code.org");
cookie.setSecure(true);
---------Resolved-----------
For local testing
We should always set the domain name to localhost
set cookie.setSecure to false as localhost is not secure protocol (not HTTPS)

Injecting custom headers into ServletRequest in Spring Boot JAVA

I have a requirement to inject custom headers into every request a spring boot application is getting, for this, I have written some code but it seems it is not doing its work. For a brief, I have implemented the Filter interface and defined the doFilter method, extended the HttpServletRequestWrapper class, and overridden getHeader() and getHeaderNames() method to take into account the custom headers I am reading from the properties file.
But, the moment I get into the controller and check the request I am not getting my custom headers that were set through the MyReqWrapper. Below is the code, I've also tried searching it in Stackoverflow but couldn't find the solution on what is/could be wrong here. Can someone point me in the right direction?
Also, please point me on how to test whether custom headers are actually set or not.
This is Filter implementation
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ReqFilter implements Filter {
private static final String CUSTOMHEADERENABLED = "customheadersenabled";
private static final String CUSTOMHEADERCOUNT = "customheaderscount";
#Autowired
private Environment env;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
boolean customHeadersEnabled = Boolean.parseBoolean(env.getProperty(CUSTOMHEADERENABLED, "false"));
int count = Integer.parseInt(env.getProperty(CUSTOMHEADERCOUNT, "0"));
if (customHeadersEnabled && count > 0) {
MyReqWrapper myReq = new MyReqWrapper((HttpServletRequest) servletRequest);
myReq.processMyHeaders(count, env);
filterChain.doFilter(customRequest, servletResponse);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
catch(ServletException ex){
throw ex;
}
}
#Override
public void destroy() {
//
}
}
This is custom request wrapper extending HttpServletRequestWrapper
final class MyReqWrapper extends HttpServletRequestWrapper {
private static final String CUSTOMHEADERPREFIX = "header1";
private final Map<String, String> myHeaders;
public MyReqWrapper(HttpServletRequest request) {
super(request);
myHeaders = new HashMap<>();
}
#Override
public String getHeader(String name) {
String headerValue = myHeaders.get(name);
if (headerValue != null){
return headerValue;
}
return ((HttpServletRequest) getRequest()).getHeader(name);
}
#Override
public Enumeration<String> getHeaderNames() {
Set<String> set = new HashSet<>(myHeaders.keySet());
Enumeration<String> headerNames = ((HttpServletRequest) getRequest()).getHeaderNames();
while (headerNames.hasMoreElements()) {
String n = headerNames.nextElement();
set.add(n);
}
return Collections.enumeration(set);
}
public void processMyHeaders(int headerCount, Environment env) {
while(headerCount > 0){
String [] headerKeyValue = Objects.requireNonNull(env.getProperty(String.format("%1$s%2$s", CUSTOMHEADERPREFIX, headerCount--)))
.split(":");
this.myHeaders.put(headerKeyValue[0], headerKeyValue[1]);
}
}
}
This was solved for me and I forgot to update this with an answer.
So the problem was I was using HttpServletRequest class from two different namespaces in the ReqFilter and controller classes, namely one from "org.apache.catalina.servlet4preview.http.HttpServletRequest" and another from "javax.servlet.http.HttpServletRequest".
Once I used uniform namespace in both the files I could access the headers from controller classes.

How to replace the authenticated javax.security.Principal in JSF context?

I want to create a "Impersonate" feature in my JSF application. This functionality would provide the Administrator with the ability to access the application authenticated with a low-level user without even knowing the password.
I though it would be a simple setUserPrincipal, similar to what I use to get the current logged in User
FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal(), but I couldn't find any "setUserPrincipal" method inside javax.faces.context.ExternalContext...
In short, what I want is to programmatically change the current logged in user, so the admin can impersonate any other user, without informing the credentials. Is it possible?
Thanks
I strongly advise against playing with authentication/authorization unless you really don't have alternatives.
Anyway, leave out JSF, it comes in the game too late.
The simplest way is to provide a customized request, supplied with a filter:
#WebFilter(filterName = "impersonateFilter", urlPatterns = "/*", asyncSupported = true)
public class ImpersonateFilter implements Filter
{
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
// do nothing
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
ImpersonateRequest impersonateRequest = new ImpersonateRequest(httpRequest);
chain.doFilter(impersonateRequest, response);
}
#Override
public void destroy()
{
// do nothing
}
public static class ImpersonateRequest extends HttpServletRequestWrapper
{
protected Principal principal;
public ImpersonateRequest(HttpServletRequest request)
{
super(request);
HttpSession session = request.getSession(false);
if(session != null)
{
principal = (Principal) session.getAttribute(ImpersonateRequest.class.getName());
}
}
#Override
public Principal getUserPrincipal()
{
if(principal == null)
{
principal = super.getUserPrincipal();
}
return principal;
}
public void setUserPrincipal(Principal principal)
{
this.principal = principal;
getSession().setAttribute(ImpersonateRequest.class.getName(), principal);
}
#Override
public String getRemoteUser()
{
return principal == null ? super.getRemoteUser() : principal.getName();
}
}
}
Something like this should suffice.

How to make Shiro return 403 Forbidden with Spring Boot rather than redirect to login.jsp

I have a server that is just an API endpoint, no client front-end, no jsp, no html. It uses Spring Boot and I'm trying to secure it with Shiro. The relevent parts of my SpringBootServletInitializer look like this. I'm trying to get Shiro to return a 403 response if it fails the roles lookup as defined in BasicRealm. Yet it seems to default to redirecting to a non-existent login.jsp and no matter what solution I seem to use. I can't override that. Any help would be greatly appreciated.
#SpringBootApplication
public class RestApplication extends SpringBootServletInitializer {
...
#Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
Map<String, String> filterChain = new HashMap<>();
filterChain.put("/admin/**", "roles[admin]");
shiroFilter.setFilterChainDefinitionMap(filterChain);
shiroFilter.setSecurityManager(securityManager());
return shiroFilter;
}
#Bean
public org.apache.shiro.mgt.SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
CookieRememberMeManager rmm = new CookieRememberMeManager();
rmm.setCipherKey(Base64.decode("XXXXXXXXXXXXXXXXXXXXXX"));
securityManager.setRememberMeManager(rmm);
return securityManager;
}
#Bean(name = "userRealm")
#DependsOn("lifecycleBeanPostProcessor")
public BasicRealm userRealm() {
return new BasicRealm();
}
#Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
}
public class BasicRealm extends AuthorizingRealm {
private static Logger logger = UserService.logger;
private static final String REALM_NAME = "BASIC";
public BasicRealm() {
super();
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token)
throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String userid = upToken.getUsername();
User user = Global.INST.getUserService().getUserById(userid);
if (user == null) {
throw new UnknownAccountException("No account found for user [" + userid + "]");
}
return new SimpleAuthenticationInfo(userid, user.getHashedPass().toCharArray(), REALM_NAME);
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
String userid = (String) principals.getPrimaryPrincipal();
if (userid == null) {
return new SimpleAuthorizationInfo();
}
return new SimpleAuthorizationInfo(Global.INST.getUserService().getRoles(userid));
}
}
OK, here is how I solved it. I created a class ...
public class AuthFilter extends RolesAuthorizationFilter {
private static final String MESSAGE = "Access denied.";
#Override
protected boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws IOException {
HttpServletResponse httpResponse ;
try {
httpResponse = WebUtils.toHttp(response);
}
catch (ClassCastException ex) {
// Not a HTTP Servlet operation
return super.onAccessDenied(request, response) ;
}
if (MESSAGE == null) {
httpResponse.sendError(403);
} else {
httpResponse.sendError(403, MESSAGE);
}
return false; // No further processing.
}
}
... and then in my shiroFilter() method above I added this code ...
Map<String, Filter> filters = new HashMap<>();
filters.put("roles", new AuthFilter());
shiroFilter.setFilters(filters);
... hope this helps someone else.
In Shiro 1.4+ you can set the login url in your application.properties:
https://github.com/apache/shiro/blob/master/samples/spring-boot-web/src/main/resources/application.properties#L20
Earlier versions you should be able to set ShiroFilterFactoryBean.setLoginUrl("/login")
https://shiro.apache.org/static/current/apidocs/org/apache/shiro/spring/web/ShiroFilterFactoryBean.html

Spring 4 Security Tiles 3 Custom Success Handler

I'm having a rough time figuring out how to redirect to a page defined in tiles configuration.
Using Spring Security 4 with annotations and Tiles 3.
The CustomSuccessHandler below works but it doesn't resolve the targetUrl to the page defined in tiles configuration.
#Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
protected void handle(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
System.out.println("Can't redirect");
return;
}
test();
redirectStrategy.sendRedirect(request, response, targetUrl);
}
static void test() {
}
protected String determineTargetUrl(Authentication authentication) {
String url="";
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> roles = new ArrayList<String>();
for (GrantedAuthority a : authorities) {
roles.add(a.getAuthority());
}
if (isAdmin(roles)) {
url = "/admin";
} else if (isUser(roles)) {
url = "/user";
} else {
url="accessDenied";
}
return url;
}
I figured out that my problem was self-inflicted, as usual. I had neglected to define "admin"or "user" above, in my views.xml (tiles configuration) file. Once I configured the pages in views.xml, it started working as expected. Thanks!

Categories