I am new to struts and spring security.
Can anyone help me to figure out how to redirect to different urls different users with different roles ? In other words, how to provide determine target url based on user role in struts2 using action controller?
I found the following question determine target url based on roles in spring security 3.1 , but I cannot figure out how to configure the action.
I tried the following setup, but it does not work:
security.xml
<form-login login-page="/login" authentication-failure-url="/login?error=true" login-processing-url="/j_security_check" default-target-url="/default"/>
struts.xml
<action name="default" class="com.moblab.webapp.action.RoleRedirectAction" method="defaultAfterLogin"/>
RoleRedirectAction.java
package com.moblab.webapp.action;
import javax.servlet.http.HttpServletRequest;
public class RoleRedirectAction extends BaseAction{
public String defaultAfterLogin(HttpServletRequest request) {
if (request.isUserInRole("ROLE_ADMIN")) {
return "redirect:/<url>";
}
return "redirect:/<url>";
}
}
Thanks a lot.
EDIT 1
I also tried the following annotation
#Action(value="/default",results={#Result(name="success",location="/querySessions")})
EDIT 2
My final solution looks like the following. I am not sure if it is the best approach, but it works:
public class StartPageRouter extends SimpleUrlAuthenticationSuccessHandler {
#Autowired
private UserService userService;
protected final Logger logger = Logger.getLogger(this.getClass());
private RequestCache requestCache = new HttpSessionRequestCache();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
//default path for ROLE_USER
String redirectPath = <url>;
if (authorities != null && !authorities.isEmpty()) {
Set<String> roles = getUserRoles(authorities);
if (roles.contains("ROLE_ADMIN"))
redirectPath = <url>;
else if (roles.contains("ROLE_INSTRUCTOR"))
redirectPath = <url>;
}
getRedirectStrategy().sendRedirect(request, response, redirectPath);
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
private Set<String> getUserRoles(Collection<? extends GrantedAuthority> authorities) {
Set<String> userRoles = new HashSet<String>();
for (GrantedAuthority authority : authorities) {
userRoles.add(authority.getAuthority());
}
return userRoles;
}
}
EDIT 3
There are even better solutions here:
http://oajamfibia.wordpress.com/2011/07/07/role-based-login-redirect/#comment-12
Assuming that you mean that you want to redirect users to different start pages depending on their assigned roles then you can try this. Note that I do all this outside of Struts.
First create your own class that extends Springs SimpleUrlAuthenticationSuccessHandler and override the onAuthenticationSuccess() method. The actual redirect is performed within the onAuthenticationSuccess() method by the line getRedirectStrategy().sendRedirect(request,response,);
So all you need is a means of substituting your own url's.
So, for example I have
package com.blackbox.x.web.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import com.blackbox.x.entities.UserDTO;
import com.blackbox.x.services.UserService;
public class StartPageRouter extends SimpleUrlAuthenticationSuccessHandler {
#Autowired
UserService userService;
#Autowired
LoginRouter router;
protected final Logger logger = Logger.getLogger(this.getClass());
private RequestCache requestCache = new HttpSessionRequestCache();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException,
ServletException {
requestCache.removeRequest(request, response);
User user = (User) authentication.getPrincipal();
UserDTO userDTO = userService.find(user.getUsername());
getRedirectStrategy().sendRedirect(request, response, router.route(userDTO));
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
}
where LoginRouter is my own class that takes the logged in user and, from the assigned roles determines which URL the user should be directed to.
You then configure Spring Security to use your version using the
authentication-success-handler-ref="customTargetUrlResolver"/>
and
<beans:bean id="customTargetUrlResolver" class="com.blackbox.x.web.security.StartPageRouter"/>
in your security context xml file.
Related
Is it possible to use authentication certificates to connect to WSO2 (CEP) Admin Services?
https://localhost:9443/services/UserAdmin?wsdl
If yes, so how to do this?
Accessing Admin Services APIs are common for all the WSO2 products. You have to use the public certificate used by the product inorder to communicate on SSL.
You may refer the following sample
import org.wso2.carbon.authenticator.stub.LoginAuthenticationExceptionException;
import org.wso2.carbon.authenticator.stub.LogoutAuthenticationExceptionException;
import org.wso2.carbon.service.mgt.stub.types.carbon.ServiceMetaData;
import org.wso2.carbon.service.mgt.stub.types.carbon.ServiceMetaDataWrapper;
import java.rmi.RemoteException;
public class ListServices {
public static void main(String[] args)
throws RemoteException, LoginAuthenticationExceptionException,
LogoutAuthenticationExceptionException {
System.setProperty("javax.net.ssl.trustStore", "$CEP_HOME/repository/resources/security/wso2carbon.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
String backEndUrl = "https://localhost:9443";
LoginAdminServiceClient login = new LoginAdminServiceClient(backEndUrl);
String session = login.authenticate("admin", "admin");
ServiceAdminClient serviceAdminClient = new ServiceAdminClient(backEndUrl, session);
ServiceMetaDataWrapper serviceList = serviceAdminClient.listServices();
System.out.println("Service Names:");
for (ServiceMetaData serviceData : serviceList.getServices()) {
System.out.println(serviceData.getName());
}
login.logOut();
}
}
For more information, check here
I would like to enable Facebook to crawl my website, however it needs user authentication. Facebook says one way to get around this is to whitelist their ips. I am using Apache Shiro and I know that you can get client's ip by calling getHost from BasicHttpAuthenticationFilter, however I do not know how to let certain ip addresses past the authentication.
You will likely have to build a custom implementation of Shrio's
org.apache.shiro.web.filter.authc.AuthenticatingFilter
Minimally, you will have to customize BasicHttpAuthenticationFilter by extending it and adding logic to skip the BasicHttpAuthenticationFilter if the request is coming from a whitelisted IP address.
package com.acme.web.filter.authc;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class WhitelistedBasicHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
private Set<String> whitelist = Collections.emptySet();
public void setWhitelist(String list) {
whitelist = new HashSet<String>();
Collections.addAll(whitelist, list.split(",")); //make sure there are no spaces in the string!!!!
}
#Override
protected boolean isEnabled (ServletRequest request, ServletResponse response) throws ServletException, IOException
{
if (whitelist.contains(request.getRemoteAddr())) {
return false;
}
return super.isEnabled(request, response);
}
}
In your 'shiro.ini'
authc=com.acme.web.filter.authc.WhitelistedBasicHttpAuthenticationFilter
authc.whitelist=192.168.1.1,192.168.1.2,192.168.2.3
I am creating a RESTful Web Service that wraps an antiquated vendor API. Some external configuration will be required and will be stored on the server either in a file or rdbms. I'm using Jersey 1.11.1 in Glassfish 3.1.2. This configuration data is all in String key/value format.
My first question is this - where can I store global/instance variables in Jersey so that they will be persisted between requests and available to all resources? If this was a pure Servlet application I would use the ServletContext to accomplish this.
The second part to the question is how can I load my configuration once the Jersey server has loaded? Again, my Servlet analogy would be to find the equivalent to the init() method.
#Singleton #Startup EJB matches your requirements.
#Singleton
#Startup // initialize at deployment time instead of first invocation
public class VendorConfiguration {
#PostConstruct
void loadConfiguration() {
// do the startup initialization here
}
#Lock(LockType.READ) // To allow multiple threads to invoke this method
// simultaneusly
public String getValue(String key) {
}
}
#Path('/resource')
#Stateless
public class TheResource {
#EJB
VendorConfiguration configuration;
// ...
}
EDIT: Added annotation as per Graham's comment
You can use a listener for init the variables and set to the context as attribute before the web application start, something like the following:
package org.paulvargas.shared;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class LoadConfigurationListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
// read file or rdbms
...
ServletContext context = sce.getServletContext();
// set attributes
...
}
public void contextDestroyed(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
// remove attributes
...
}
}
This listener is configured in the web.xml.
<listener>
<listener-class>org.paulvargas.shared.LoadConfigurationListener</listener-class>
</listener>
You can use the #Context annotation for inject the ServletContext and retrieving the attribute.
package org.paulvargas.example.helloworld;
import java.util.*;
import javax.servlet.ServletContext;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
#Path("/world")
public class HelloWorld {
#Context
private ServletContext context;
#GET
#Produces("text/plain; charset=UTF-8")
public String getGreeting() {
// get attributes
String someVar = (String) context.getAttribute("someName")
return someVar + " says hello!";
}
}
I have several independent Java EE modules (WAR web applications, and JAR EJB modules) which I deploy on JBoss 7.1.1 AS.
I want to:
Centralize configuration of these modules in one *.properties file.
Make this file available in classpath.
Keep the installation/configuration of this file as simple as possible. Ideally would be just to put it in some JBoss folder like: ${JBOSS_HOME}/standalone/configuration.
Make changes to this file available without restarting the application server.
Is this possible?
I already found this link: How to put an external file in the classpath, which explains that preferable way to do this is to make static JBoss module. But, I have to make dependency to this static module in every application module that I deploy, which is a kind of coupling I'm trying to avoid.
Maybe a simple solution is to read the file from a singleton or static class.
private static final String CONFIG_DIR_PROPERTY = "jboss.server.config.dir";
private static final String PROPERTIES_FILE = "application-xxx.properties";
private static final Properties PROPERTIES = new Properties();
static {
String path = System.getProperty(CONFIG_DIR_PROPERTY) + File.separator + PROPERTIES_FILE;
try {
PROPERTIES.load(new FileInputStream(path));
} catch (MalformedURLException e) {
//TODO
} catch (IOException e) {
//TODO
}
}
Here is a full example using just CDI, taken from this site.
This configuration will also work for JBoss AS7.
Create and populate a properties file inside the WildFly configuration folder
$ echo 'docs.dir=/var/documents' >> .standalone/configuration/application.properties
Add a system property to the WildFly configuration file.
$ ./bin/jboss-cli.sh --connect
[standalone#localhost:9990 /] /system-property=application.properties:add(value=${jboss.server.config.dir}/application.properties)
This will add the following to your server configuration file (standalone.xml or domain.xml):
<system-properties>
<property name="application.properties" value="${jboss.server.config.dir}/application.properties"/>
</system-properties>
Create the singleton session bean that loads and stores the application wide properties
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
#Singleton
public class PropertyFileResolver {
private Logger logger = Logger.getLogger(PropertyFileResolver.class);
private String properties = new HashMap<>();
#PostConstruct
private void init() throws IOException {
//matches the property name as defined in the system-properties element in WildFly
String propertyFile = System.getProperty("application.properties");
File file = new File(propertyFile);
Properties properties = new Properties();
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
logger.error("Unable to load properties file", e);
}
HashMap hashMap = new HashMap<>(properties);
this.properties.putAll(hashMap);
}
public String getProperty(String key) {
return properties.get(key);
}
}
Create the CDI Qualifier. We will use this annotation on the Java variables we wish to inject into.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR })
public #interface ApplicationProperty {
// no default meaning a value is mandatory
#Nonbinding
String name();
}
Create the producer method; this generates the object to be injected
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
public class ApplicationPropertyProducer {
#Inject
private PropertyFileResolver fileResolver;
#Produces
#ApplicationProperty(name = "")
public String getPropertyAsString(InjectionPoint injectionPoint) {
String propertyName = injectionPoint.getAnnotated().getAnnotation(ApplicationProperty.class).name();
String value = fileResolver.getProperty(propertyName);
if (value == null || propertyName.trim().length() == 0) {
throw new IllegalArgumentException("No property found with name " + value);
}
return value;
}
#Produces
#ApplicationProperty(name="")
public Integer getPropertyAsInteger(InjectionPoint injectionPoint) {
String value = getPropertyAsString(injectionPoint);
return value == null ? null : Integer.valueOf(value);
}
}
Lastly inject the property into one of your CDI beans
import javax.ejb.Stateless;
import javax.inject.Inject;
#Stateless
public class MySimpleEJB {
#Inject
#ApplicationProperty(name = "docs.dir")
private String myProperty;
public String getProperty() {
return myProperty;
}
}
I am running a JSE application with Restlet 2.1. I am attempting to use the application context, and am finding that it is always null in my application. Because it is null, I cannot seem to access anything -- including any attributes that I pass when I invoke the resource.
The code for the restlet application class is below:
package net.factor3.mailapp;
import net.factor3.mailapp.impl.PageServerImpl;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.MediaType;
import org.restlet.data.Protocol;
import org.restlet.routing.Router;
import org.restlet.routing.Template;
public class MyServer extends Application
{
public MyServer()
{
setName("Test Application");
setDescription("Testing use of Restlets");
}
#Override
public Restlet createInboundRoot()
{
Context ctx = getContext();
Router route = new Router(ctx);
route.setDefaultMatchingMode(Template.MODE_EQUALS);
route.attach("http://localhost:8100/",PageServerImpl.class);
route.attach("http://localhost:8100/{page}",PageServerImpl.class);
return(route);
}
public static void main(String[] args) throws Exception
{
Server asrv = new Server(Protocol.HTTP,8100);
asrv.setNext(new MyServer());
asrv.start();
}
}
Note that the PageServerImpl is a ServerResource. In createInboundRoot(), I use getContext() to get the application's context and put it into ctx. ctx is always null, and I believe for that reason parameters and attributes are lost in the ServerResource.
Is this a bug in the JRE version of Restlet 2.1? If it is, where do I go to report it? There is no clear link to bug reports on the Restlet website.
If it is not a bug, then how do I get a decent context in an application of this kind???
Someone please advise.
Using Component Class you can create statisfy your need:
public class MyServer extends Application
{
public MyServer()
{
setName("Test Application");
setDescription("Testing use of Restlets");
}
public static void main(String[] args) throws Exception
{
// Create a new Restlet component and add a HTTP server connector to it
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
// Then attach it to the local host
component.getDefaultHost().attach("/trace", GenericResource.class);
// Now, let's start the component!
// Note that the HTTP server connector is also automatically started.
component.start();
}
}