I am new to Swagger and have implemented Swagger UI with spring mvc, I want to disable UI on production/live environment. I am struggling to figure it out.
This is what I am using
SwaggerConfig.java
#Configuration
#EnableSwagger2
public class SwaggerConfig {
}
RestController.java
#RestController
#Api(value="city", description="Operations pertaining to City Data")
#RequestMapping(value="/v1/city")
public class RestController {
#ApiOperation(value = "View city by stateId or stateName")
#RequestMapping(value="/search",method=RequestMethod.POST)
public ResponseEntity<Object> getCityBystateId(#RequestBody StateDto stateDto,Model model){
}
}
Look into the Spring's profile mechanism that lets you register different beans in different environments
When you bootstrap it according to doc, you can annotate your swagger config class on a class level with e.g. #Profile("dev"), thus enabling the swagger configuration for the environment of your choice
#Configuration
#EnableSwagger2
#Profile("dev")
public class SwaggerConfig {
}
Another way is to use a reverse proxy to reject access to Swagger Api on your production environment.
In this case your production installation is exactly the same as your development/test environment (then more compliant with DevOps method) and you can continue to access your Swagger API with internal calls.
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 am trying to load the required parameters like accessURI,client_id, client_secret from properties file like below. It never loads the properties, I see many sites mention this as example. It is working if I tries to set it explicitly.
In this example
https://dzone.com/articles/build-a-spring-boot-app-with-secure-server-to-serv
#Bean()
#ConfigurationProperties(prefix ="my.oauth2.client")
protected ClientCredentialsResourceDetails oAuthDetails() {
return ClientCredentialsResourceDetails();
But it never loaded the properties so I need to change to use set methods.I am not able to figure out why it did not load, I believe I cannot mention this in #EnableConfigurationProperties as it does not have the configuration configured.
I tried searching but could not find a matching reason.
Basically, the binding of properties to class members works as described in the example.
The properties have to be present either as YAML or properties file:
spring:
security:
oauth2:
client:
registration:
oauth:
client-id: XXXXXX
client-secret: YYYYYYY
scope: openid
redirect-uri: http://redirect
access-token-uri: https://token
client-authentication-method: basic
authorization-grant-type: client_credentials
Then the #ConfigurationProperties annotation has to be declared with the correct prefix. In this example it is "spring.security.oauth2.client.registration.oauth".
#Bean
#ConfigurationProperties(prefix ="spring.security.oauth2.client.registration.oauth")
protected ClientCredentialsResourceDetails oAuthDetails() {
return new ClientCredentialsResourceDetails();
}
As stated in the Spring Boot documentation you normally do not have to set the #EnableConfigurationProperties annotation unless you are developing your own auto-configuration.
But if you are working on a non Spring Boot project the #EnableConfigurationProperties annotation has to be declared, describing the class where we want to use the #ConfigurationProperties annotation:
#Configuration()
#EnableConfigurationProperties(MyProperties.class)
public class MyConfig {
}
with
#ConfigurationProperties(prefix="myproperties")
public class MyProperties {
}
Alternatively you can also use configuration property scanning which works similarly to the component scanning by declaring the #ConfigurationPropertiesScan annotation:
#SpringBootApplication
#ConfigurationPropertiesScan({ "my.app" })
public class MyApplication {
}
Hope, this helps. Pero
*I need to invoke a spring service from my java class, how would i set the active profile dynamically for spring service.
here's the code
java code
public void abc() {
AccountDetailService service = new AccountDetailService();
service.getAccountDetails();
}
AccountDetailService
#Profile
#Log
#Component
private void getAccountDetails() {
String filename=environment.getProperty("fileName");
accountDaoImpl.getDetails(filename);
}
i have various profiles like dev,qa and prod
how would i pass active profiles from my java class when invoking spring service.*
You don't need to pass any profiles to your service. Simply, create configurations for different profiles. These configurations will provide the correct service instance for given profile. Here you have an example of such configuration class:
#Configuration
#Profile(value = "test")
public class ServiceTestConfig
{
#Bean
public Service service()
{
return new TestService();
}
}
Create another configuration with other #Profile annotation and Spring will automatically create proper instances. You can set active profile in many ways, the simplest is to change the spring.profiles.active property in your application.properties. Refer to Spring documentation to learn more about profiles:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
Update
If you really need to pass your active profile to your service in runtime, you can inject Environment instance and call getActiveProfiles(). See the javadoc: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/env/Environment.html. Just keep in mind it's not the way profiles should be used in Spring. The example I provided before is considered the best practice.
In a mixed Java/Scala Spring (v3.2) project I want to create a scala-based controller, and secure it with #Secure annotation. For java controllers this works just fine, but when I add #Secure annotation to the scala controller it disappears from URL mapping on application startup.
Sample code:
#Controller
#Secured(Array("ROLE_USER"))
class TestController {
#RequestMapping(value = Array("/show"), method = Array(RequestMethod.GET))
def show = {
"helloTemplate"
}
}
The same if I put secure annotation per method - the whole controller class will disappear from URL mapping (even if there are unsecured methods). No exceptions or warnings in the log. If I secure this URL via spring-security intercept-url in xml configuration - all things works fine (without #Secure annotation on controller). Spring and spring-security configuration via xml files and annotation-driven configuration is turned on. Thank you for any help.
In Spring 4/Spring you must enable proxyTargetClass = true to use CGLIB class proxying. This is superior to java class Proxying when working with scala.
SpringBoot
#EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
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 ...")