In a web project we have a simple file browser. For security we need some kind of mapping what user roles can read (R) or read+write (RW) in which directories (including their subdirectories).
examples:
UserA with roleA can view files in folderA/
roleA, "folderA/", R
UserB with roleB view and edit files in folderA/ and also view folderB/
roleB, "folderA/", RW
roleB, "folderB/", R
Some of the used technologies in the project so far:
Tapestry
Hibernate
Spring security (only used for users and roles so far)
I've been looking further into Spring Security, but am not sure if I can use a GrantedAuthority implementation (permissions) for my use case.
So my question is if anybody has experience with Spring Security and can point me in the right way. If it's not possible with Spring Security, other library proposals are also appreciated, but I ofcourse prefer using a library that is already being used.
It is possible with Spring Security with some custom code. Annotate your service layer to something like:
#PostAuthorize("hasPermission(returnObject, 'READ')")
public FileObject readFileObject(String path) {
// return the file object from DAO
}
#PreAuthorize("hasPermission(#fileObj, 'WRITE')")
public void writeFileObject(FileObject fileObj) {
// write the file object, security check made at this point
}
Implement the PermissionEvaluator interface, make it a Spring bean. Then enable it in security config with:
#Autowired
private PermissionEvaluator permissionEvaluator;
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler() {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
And:
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
Related
So I have a React app I want to serve from my Spring app (ala this blog). As part of my gradle build task, I run the npm build command and copy the resulting files to /build/resources/main/static. This works fine and I can access my app at mysite.com/index.html, but I want to control who has access more granularly. As such, I applied #EnableWebMvc to my app, but from there, I can't seem to get my API controller to actually serve the view from the build directory. It seems no matter where I put it, it doesn't like serving directly from /build. Any way to make this work?
The handler looks like:
#Controller
class MyController {
#RequestMapping("/")
fun index(): String {
return "index"
}
}
As indicated in the Spring Boot documentation, you do not need - in fact, it is not recommended - to use #EnableWebMvc when using Spring Boot. They state, when describing Spring MVC auto-configuration:
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
And:
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own #Configuration class of type WebMvcConfigurer but without #EnableWebMvc.
In the guide, they continue when describing static content handling:
By default, Spring Boot serves static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so that you can modify that behavior by adding your own WebMvcConfigurer and overriding the addResourceHandlers method.
In your example, following this advice, you can indicate the static resource handling location with something like (sorry, I am not fluent in Kotlin, forgive for write the example in Java):
#Controller
public class MyController implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static")
;
}
#GetMapping(path = "/")
public String index() {
return "index";
}
}
Please, adapt the paths in addResourceHandlers to your needs.
You can of course place this method in an ad hoc #Configuration.
Having said that, if when you say granular you mean security, the best approach you can take is to configure Spring Security and provide the necessary authorization rules: please, see the relevant documentation.
I'm currently developing multi-tenant application using spring and using oauth. Every tenant will have different database. If my url is tenant1.xxx.com, then it will use tenant1 database, etc.
My request already successfully routed using AbstractRoutingDataSource but not the authentication. So when I ask an access token, it still using the default datasource. I think my problem is in oauth2 configuration that set datasource like TokenStore, etc.
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
Is there anyway that token store can choose datasource according to current tenant?
Let me try to give solutions
1) If you check to code inside the JdbcTokenStore then we will find that it's using inside the JdbcTempaltes so JPA or Hibernate routing will not work for that in that case a part of the solution you have to implements org.springframework.security.oauth2.provider.token.TokenStore with JPA implementation.
2) If you do not like to do a solution No 1 then you can also implement AbstractRoutingDataSource and while creating TokenStore passed the routing data source.
For the time being I see no reason to add Redis but all Spring Session examples include it. I want to design with the idea in mind that I might add it later. The thing I want right now is Header Authentication.
How can I enable the Header Authentication without enabling Redis?
(a spring boot single file application as example would be nice)
The MapSessionRepository was created for just that purpose.
This works with version 1.2 and untested, 1.1
#EnableSpringHttpSession
class HttpSessionConfig {
#Bean
MapSessionRepository sessionRepository() {
return new MapSessionRepository();
}
#Bean
HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
}
I think all you need is a filter of type - SessionRepositoryFilter<Session>, which in a Spring Boot application means a #Bean of that type. When you create it you just inject a HeaderHttpSessionStrategy.
I'm deploying Spring based web applications on Amazon's Beanstalk platform, and they give me the option of setting a "healthcheck" URL path for my application.
The idea is that their platform will do a request against that URL, after the deployment, to see if the application is successfully started. So, if the request results in an HTTP 200, the application is probably fine. But if it results in an HTTP 500 or something else, the platform knows there's a problem with the application.
So, I wish I could develop some kind of servlet that would check if the Spring Application Context was successfully initialised, or not, to give an appropriate HTTP response code to the platform.
Has anybody attempted something like this? For similar purposes?
I'm wondering if Spring already provides some elegant solution for this.
I'd suggest using health checks functionality from Metrics. You could set up a number of classes that extend HealthCheck class and implement check() method. These health check implementations would be Spring managed beans themselves and could autowire Spring beans and validate them. Then configure HealthCheckServlet to monitor application state. Also check metrics-spring project. It will make Spring and Metrics integration simpler.
If you are using Java Spring configuration you might have a Metrics config like this that extends MetricsConfigurerAdapter from metrics-spring
#Configuration
#EnableMetrics
public class MetricsConfig extends MetricsConfigurerAdapter { }
And then #Import(value = {MetricsConfig.class}) to your Spring config.
You also need and implementation of ServletContextListener to wire up HealthCheckServlet and Spring. This HealthCheckContextListener should be added to your web.xml
public class HealthCheckContextListener extends
HealthCheckServlet.ContextListener implements ServletContextListener {
private WebApplicationContext context;
public HealthCheckContextListener(WebApplicationContext context) {
this.context = context;
}
public HealthCheckContextListener() {}
#Override
public void contextInitialized(ServletContextEvent event) {
this.context = WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext());
event.getServletContext().setAttribute(HealthCheckServlet.HEALTH_CHECK_REGISTRY,
context.getBean(HealthCheckRegistry.class));
}
#Override
protected HealthCheckRegistry getHealthCheckRegistry() {
return (HealthCheckRegistry) context.getBean(HealthCheckRegistry.class);
}
}
The simplest thing you can do is this:
#Controller
class HealthCheckController {
#ResponseStatus(OK)
#RequestMapping(value = "/ping", method = HEAD) {
public void ping() {
}
}
Extendable to also test particular beans, DataSources etc.
You should consider what constitutes a healthy app for you (e.g., servlet tier? JMS queues? FTP servers? etc.) and have your health check verify those services' availability. Obviously the health check is going to run frequently, so you don't want to initiate expensive operations over and over again.
Spring Boot is a new project that aims to simplify Spring development by favoring convention instead of configuration. They have implemented a "health check" feature that you can add to a project via an Actuator add-in module.
Here's a reference to their Health Check implementation -- it uses a controller class to return "ok" and, if there is a data source, attempts to run a query to confirm that the database is accessible (something like "SELECT .. from dual" in Oracle syntax).
This can easily be done in the spring boot framework. By adding below dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
You can check the service by hitting the below URL.
localhost:serverPort/actuator/health
Can Spring Security use #PreAuthorize on Spring controllers methods?
Yes, it works fine.
You need <security:global-method-security pre-post-annotations="enabled" /> in ...-servlet.xml. It also requires CGLIB proxies, so either your controllers shouldn't have interfaces, or you should use proxy-target-class = true.
See Spring Security FAQ (emphasis mine).
In a Spring web application, the application context which holds the
Spring MVC beans for the dispatcher servlet is often separate from the
main application context. It is often defined in a file called
myapp-servlet.xml, where “myapp” is the name assigned to the Spring
DispatcherServlet in web.xml. An application can have multiple
DispatcherServlets, each with its own isolated application context.
The beans in these “child” contexts are not visible to the rest of the
application. The “parent” application context is loaded by the
ContextLoaderListener you define in your web.xml and is visible to all
the child contexts. This parent context is usually where you define
your security configuration, including the
element). As a result any security constraints applied to methods in
these web beans will not be enforced, since the beans cannot be seen
from the DispatcherServlet context. You need to either move the
declaration to the web context or moved the
beans you want secured into the main application context.
Generally we would recommend applying method security at the service
layer rather than on individual web controllers.
If you apply pointcuts to service layer you only need to set <global-method-security> in your app's security context.
If you're using Spring 3.1, you can do some pretty cool stuff with this. Take a look at https://github.com/mohchi/spring-security-request-mapping. It's a sample project that integrates #PreAuthorize with Spring MVC's RequestMappingHandlerMapping so that you can do something like:
#RequestMapping("/")
#PreAuthorize("isAuthenticated()")
public String authenticatedHomePage() {
return "authenticatedHomePage";
}
#RequestMapping("/")
public String homePage() {
return "homePage";
}
A request for "/" will call authenticatedHomePage() if the user is authenticated. Otherwise it will call homePage().
It's over two years since this question was asked but because of problems I had today I'd rather discourage using #Secured, #PreAuthorize, etc. on #Controllers.
What didn't work for me was #Validated combined with #Secured controller:
#Controller
#Secured("ROLE_ADMIN")
public class AdministrationController {
// #InitBinder here...
#RequestMapping(value = "/administration/add-product", method = RequestMethod.POST)
public String addProductPost(#ModelAttribute("product") #Validated ProductDto product, BindingResult bindingResult) {
// ...
}
Validator simply does not fire (Spring MVC 4.1.2, Spring Security 3.2.5) and no checks are performed.
Similar problems are caused by CGLIB proxies used by Spring (when there is no interface implemented by a class, Spring creates CGLIB proxy; if the class implements any interface then JDK Proxy is generated - documentation, well explained here and here).
As mentioned in the answers that I linked above, is't better to use Spring Security annotations on service layer that usually implements interfaces (so JDK Proxies are used) as this does not lead to such problems.
If you want to secure web controllers, the better idea is to use <http> and <intercept-url /> that are bound to specific urls rather than methods in controllers and work pretty well. In my case:
<http use-expressions="true" disable-url-rewriting="true">
...
<intercept-url pattern="/administration/**" access="hasRole('ROLE_ADMIN')" />
</http>
There is already a reply regarding how to make it work by changing xml configuration; however, if you are working with code-based configuration, you can achieve the same by placing the following annotation over your #Configuration class:
#EnableGlobalMethodSecurity(prePostEnabled=true)
To Extend the answer provided by Andy, you can use:
#PreAuthorize("hasRole('foo')")
to check the specific role.
step1: add #EnableGlobalMethodSecurity(prePostEnabled = true) annotation in SecurityConfig class .
like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
.....
}
step 2: you can add #PreAuthorize() annotation in controller or service or repository layer. in a method or class level.
for example:
#RestController
#PreAuthorize("isAuthenticated()")
public class WebController {
#PreAuthorize("permitAll()")
#GetMapping("/")
public String home() {
return "Welcome home!";
}
#GetMapping("/restricted")
public String restricted() {
return "restricted method";
}
}
or
#RestController
public class AdminController {
#PreAuthorize("hasRole('ADMIN')")
#GetMapping("/admin")
public String adminMethod() {
}
}
First you need to add this annotation in your WebSecurityConfig to enable #Pre and #Post annotations.
#EnableGlobalMethodSecurity(prePostEnabled = true)
You can also check roles/authorities as follows
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
equivalent to
#PreAuthorize("hasRole('ROLE_ADMIN')")
You can also check multiple roles/authorities as follows
#PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER') or ...")