Some questions about Apache Shiro in Spring MVC - spring-mvc

I downloaded a Spring MVC project which using Apache Shiro for security layer. In the controller, it uses #RequiresPermissions to define the permission, for example:
#RequiresPermissions("sys:user:view")
#RequestMapping(value = {"index"})
public String index(User user, Model model) {
return "modules/sys/userIndex";
}
#RequiresPermissions("sys:user:view")
#RequestMapping(value = {"list", ""})
public String list(User user, HttpServletRequest request, HttpServletResponse response, Model model) {
return "modules/sys/userList";
}
I have couple of questions about this:
What kind of permission is this? I checked the Shiro documents, based on the doc, three parts should be "domain:action:instance", but in the code above, the first two parts are path, and the last part is the action. So I'm just confused.
I'm not sure whether the annotation #RequiresPermissions is using to define the permission. I tried to use that define a new permission, but failed. If it's not, how to define a new permission?

The actual permission String is freeform. "domain:action:instance" is an example. You could use something like users:write:1234 or just more general users:write. But there is nothing stopping you from using something like <domain>:<instance>:<action>. Using the same two examples you would have users:1234:write and users:*:write (respectively).
As for #2 your realm (or a RolePermissionResolver) is responsible for defining the mapping between users and permissions (or roles and permissions)

Related

What's wrong with my spring-social ConnectController?

I'm trying to make a Spring Boot app that will connect to Fitbit's api using spring-social. I've (half-way) implemented a ConnectionFactory and it's dependencies for Fitbit, and am trying to consume it from my app. Part of this involves starting up a ConnectController to handle the OAuth2 "dance".
When I try to hit the ConnectController through my browser at http://localhost:8080/connect or http://localhost:8080/connect/fitbit I get redirected to the whitelable error page with the message:
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
I don't really understand what I'm seeing, but when I set DEBUG level logging and use some breakpoints it looks like hitting /connect from the browser results in Spring trying to find something called connect/status and hitting /connect/fitbit result in spring trying to find something named /connect/fitbitConnect and then trying to internally make a GET request to /connect/connect/fitbitConnect.
In both cases it looks like the methods on ConnectController corresponding to /connect and /connect/{providerId} get called fine, and then Spring bombs when it goes looking for all that other stuff.
Here is the SocialConfigurer implementation I'm using which creates the ConnectController bean:
#Configuration
#EnableSocial
#PropertySource("${properties.path}/fitbot-service.properties")
public class SpringSocialConfig implements SocialConfigurer{
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
connectionFactoryConfigurer.addConnectionFactory(new FitbitConnectionFactory(
environment.getProperty("fitbit.clientId"),
environment.getProperty("fitbit.clientSecret")
));
}
#Override
public UserIdSource getUserIdSource() {
return new SessionUserIdSource();
}
#Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
return new InMemoryUsersConnectionRepository(connectionFactoryLocator);
}
#Bean
public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
return new ConnectController(connectionFactoryLocator, connectionRepository);
}
}
What on earth is going on here? What am I doing wrong?
I believe this to be related to your question regarding GET vs. POST in ConnectController, so you may have already answered this for yourself. Nonetheless, let me clarify why it's looking for connect/status and connect/fitbitConnect.
Those are view names. When you do a GET for /connect, you're asking ConnectController to fetch connection status for all providers and to place it in the model, after which it will forward that model to a view whose logical name is "connect/status". Usually this is a JSP at the path "/connect/status.jsp" or maybe a Thymeleaf template at "/connect/status.html", but it can be any view following the rules of whatever Spring MVC view resolvers are in play.
Likewise, a GET request for /connect/fitbit is asking ConnectController to fetch connection status for the "fitbit" provider and to place that information in the model and forward it on to a view whose name is "/connect/fitbitConnect" (if there isn't a connection) or "/connect/fitbitConnected" (if there is a connection).
Aside from answering your question, may I also request that you tell me more about your FitBit Spring Social provider project? Is it modeled after other community-led Spring Social projects? In other words, is it a standalone extension to Spring Social that others may use? If so, tell me where it is in GitHub and I'll be happy to add it to the "Community Projects" section at http://projects.spring.io/spring-social/.

Handle cookie in Spring MVC

I have an Spring MVC application which using the classic three layer: controller service and dao.
And the related models in the application contains User Department Project.
One user will belong to a department, and there may be a lot of projects belong to a certain department, and the departments are organized as a tree like structure, for example:
dep1
dep1-1
dep1-1-1
dep1-1-2
...
...
...
Now I have a controller to list the projects:
class ProjectController{
private ProjectService projectService;
#RequestMapping("/list")
public String list(#RequestParameter("depId") String depId){
projectService.list(depId);
return "list";
}
}
ProjectServiceImpl implements ProjectService{
ProjectDao projectDao;
public List<Department> list(String depId){
}
}
It seems that this is rather simple, however we have two problems:
1 The result filter.
According to the configuration, the department of the current user maybe(or not) be under consideration during the query operation, for example, when the parameter depId is dep1-1-1, and the current user belongs to dep1-1-2, then we should return null.
As said, this feature maybe closed at all at some situation.
2 The authentication.
The user authentication and management is served in another application which will deployed at the same domain with my application, you can think they are two different folder inside the /tomcat/webapps. We use cookie to share the user information:save a token for a user.
Which means for every request, I will have to get the token of the current user(if they have login) from the cookie, and then call the service provided by the other application to get the information like department and etc.
So where to do the department check, in controller or service? I am not sure if inject the HttpRequest to service is a good idea or not.
Also Since there are too many controllers and services related to this kind of operation, I want to avoid the duplicate codes everywhere.
Is there any better choices?
It may be overkill, but what you are asking for make me think to spring-security :
authentication : spring security could do it directly, but you can also implement a custom PreauthenticatedAuthenticationFilter that would use the cookie to get the user info and populates a Spring Security Authentication token. This part is easy and highly configurable.
result filter : extract from the spring security reference manual :
Spring Security supports filtering of collections and arrays and this can now be achieved using expressions. This is most commonly performed on the return value of a method. For example:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();
When using the #PostFilter annotation, Spring Security iterates through the returned collection and removes any elements for which the supplied expression is false
All this is implemented through Spring AOP so it is easy to add those annotations on service methods.
Spring Security integrates nicely in a Spring MVC application. The only problem is that a full configuration including domain object security is not really trivial ...

Spring Social Facebook

I am developing with Spring Social and Thymeleaf from the quick start example, but I realised that it only supports one Facebook object per controller. This means the sample can't provide support for multiple users and I am guessing it has to do with the #Scope of the variable. Its runs in a Spring boot container and I wonder how I can configure this so that each session has its own Facebook object.
As you suggested, the Facebook object should be configured with request scope. If you're using the configuration support and/or Spring Boot, then it will be request scoped. Therefore, even though the controller is injected once with a Facebook instance, that instance is really a proxy that will delegate to a real FacebookTemplate instance that is created at request time for the authenticated user.
I can only assume that you're referring to the getting started guide example at http://spring.io/guides/gs/accessing-facebook/. In that case, it's using the most simple Spring Boot autoconfiguration possible for Spring Social, which includes a basic (yet not intended for production) implementation of UserIdSource which always returns "anonymous" as the user ID. Therefore, after you create the first Facebook connection, the second browser tries to find a connection for "anonymous", finds it, and gives you an authorized Facebook object.
This may seem peculiar, but it is an example app intended to get you started...and it does that. All you need to do to get a real UserIdSource is to add Spring Security to the project. That will tell Spring Social autoconfiguration to configure a UserIdSource that fetches the current user ID from the security context. This reflects a more real-world use of Spring Social, albeit obviously more involved and beyond the scope of the getting started guide.
But you can look at https://github.com/spring-projects/spring-social-samples/tree/master/spring-social-showcase-boot for a more complete example of Spring Social within Spring Boot.
Spring Boot autoconfigures a lot of things behind the scenes. It does autoconfigure the Facebook, LinkedIn and Twitter properties and sets up the connection factories for social providers.
However, the implementation of UserIdSource always returns “anonymous” as the user ID. Once the first Facebook connection is established the second browser will try to find a connection for “anonymous” which it finds and gives you an authorised Facebook object.
#Configuration
#EnableSocial
#ConditionalOnWebApplication
#ConditionalOnMissingClass("org.springframework.security.core.context.SecurityContextHolder")
protected static class AnonymousUserIdSourceConfig extends SocialConfigurerAdapter {
#Override
public UserIdSource getUserIdSource() {
return new UserIdSource() {
#Override
public String getUserId() {
return "anonymous";
}
};
}
}
Solution
The solution is to override the “anonymous” as the UserId for each new user/session. So for each session, we can simply return a SessionID, however, it may not be unique enough to identify users, especially if it’s being cached or stored somewhere in a connection database.
#Override
public String getUserId() {
RequestAttributes request = RequestContextHolder.currentRequestAttributes();
String uuid = (String) request.getAttribute("_socialUserUUID", RequestAttributes.SCOPE_SESSION);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
request.setAttribute("_socialUserUUID", uuid, RequestAttributes.SCOPE_SESSION);
}
return uuid;
}
The solution for above problem has been talked about in detail over here

What is the correct approach for denying access to specific resources in spring MVC + security

I've seen many questions requesting how to handle security scenarios, all either have solutions for method annotations (i.e. #PreAuthorize("hasRole('ROLE_USER')")) or using a point-cut.
However what if the resource isn't known if the user has access until the resource has been read from a data store? Let's consider a user who has access to a set of customers, the rest endpoint for these customers can be found at /customers/{id}. A user is only allowed access if they have been granted access to read the account, likewise they must also have access to make a POST to the same endpoint.
One way would be:
#RequestMapping(value = "/customers/{id}", method = RequestMethod.GET)
public ModelAndView customerPage(String id, HttpServletRequest req, Principal principal) {
if (!req.isUserInRole("ROLE_ADMIN") && !cs.accessGranted(id, principal.getName())) {
throw new AccessDeniedException("You do not have access to view this custoemr.");
}
Customer cust = cs.getCustomer(id);
if (cust == null) {
throw new ResourceNotFoundException("Customer does not exist!");
}
ModelAndView mov = new ModelAndView("customers/info");
mov.addObject("customer", cust);
return mov;
}
I'm wondering if this is the right approach though.
UPDATE: Call to accessGranted was meant to have id as an argument which I missed.
There is a way to continue use #PreAuthorize annotations. You can call beans directly from SpEL expression:
#PreAuthorize("hasRole('ROLE_USER') and !#cs.accessGranted(#principal.getName())")
public ModelAndView customerPage(String id, HttpServletRequest req, Principal principal) {
#cs refers to bean id = "cs" declared somwhere in your application context. Later you can future simplify it by removing Principal principal method parameter and getting username directly in SpEL.
If your find yourself using this tehnique often then check out Spring Security ACL module.
My favorite way is to use the #Secured annotation on a method, which takes an array of Strings representing the Role(s) required to execute the method. I like this method because you are not limited to putting security only on URL patterns. For instance, you can add this to a method in your Service class, and any Controller using that Service is now secured.
The other common method is to include URL filters in the Spring Security XML file. I forget the exact syntax, but you basically setup filters that match a URL and indicate what Role(s) are needed.

What is the most unobtrusive way to add a layer of security for a private beta of website?

Let's say I have an ASP.NET site (MVC in this case) that uses Forms authentication and a typical membership system. The site allows both authenticated and anonymous users.
When I release the site as a private beta I want to add another layer of security on top of the application, like superuser's simple password system, for example. Once a user has passed this layer of security, I still want my forms authentication/membership system in place so beta testers can view the site as authenticated or anonymous users.
What's the most unobtrusive way to achieve this? I'm looking for the easiest solution that will require the least amount of new or modified code. E.g. I don't want to modify every controller to check for a special cookie. There must be a better way...
There's a very similar question here, but it seems the site in question (once public) will only serve anonymous requests, so it doesn't necessarily compare to my situation. This answer suggests ServerFault used some cookie system, but there are no further details about how it might have been implemented.
Implement security at server level, in IIS and add the accounts/passwords in Active Directory of Windows running the IIS server.
You won't need to change any of the code.
Well, I know you don't want to modify your current controllers but here's what I did for a similar behaviour.
I've created a custom ActionFilterAttribute that I've given to every controller that requires to have that specific access check. You can have something like this :
public class CheckBetaAccess : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (!canAccess) {
filterContext.Controller.ViewData["someViewData"] = "some text";
filterContext.Result = new ViewResult {
ViewName = "the-view-anonymous-users-should-see",
ViewData = filterContext.Controller.ViewData
};
filterContext.Result.ExecuteResult(filterContext);
}
}
}
Then I decorated my controllers :
[CheckBetaAccess]
public class SomeController : Controller {
//....
}

Resources