I use swagger2. I configured it to redirect to swagger-ui.html when the use access the root path.
#Bean
public WebMvcConfigurer index() {
return new WebMvcConfigurer() {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/", "/swagger-ui.html");
}
};
}
As I configured in application.yml the context-path as /api, now the when user tries to access /api he is redirected to /api/swagger-ui.html.
But I don't want to display swagger-ui.html at all and leave only /api. Is there a way to achieve that?
you can use below java script on the landing html page
<script>
history.replaceState('data to be passed', 'Title of the page', "/api/");
</script>
Related
I'm doing a Spring web-application project as a final project for school. We use SpringBoot with Hibernate and an H2 database. In order to authenticate and authorize, we use Spring Security. We also use the model View-Controller with repositories, services, and controllers.
I have an initial front page (with the URL "/") that is shown when you open the project. Once you log-in, you're redirected to another front page with the URL ("/portadaUsuario")(I'm from Spain so there's a lot of names in Spanish). But, if you somehow end up in the ("/") URL after you've logged in, you're shown the same front page that is shown to non-logged users with the sign-up and log-in options, which is obviously wrong.
I know that I can show different HTML elements with spring security tags and elements, but I've already built my project around having two different front-pages and I would like to keep it that way if possible. If it isn't achievable, please let me know how should I proceed to show different elements in the same HTML file.
Here are the methods of my WellcomeController
#Controller
public class PortadaController {
#GetMapping({"/"})
public String mostrarPortada() {
return "portada";
}
#GetMapping("/portadaUser")
public String mostrarPortadaUsuario() {
return "/usuario/portada";
}
}
Here are the methods that authenticate the users
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**","/js/**","/webjars/**", "/h2-console/**", "/", "/newUser/**").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(customSuccessHandler)
.defaultSuccessUrl("/portadaUser")
.and()
.logout()
.logoutUrl("/logout")
.permitAll()
.logoutSuccessUrl("/")
.and()
.exceptionHandling()
.accessDeniedPage("/acceso-denegado");
http.csrf().disable();
http.headers().frameOptions().disable();
}
What I want is the application detecting when a logged user is requesting the "/" URL, and for it to be redirected to /portadaUser instead.
Assuming, you have two pages, one for signed in (signed-in-home) and another for not signed in(not-signed-in-home), you code something like this -
#GetMapping("/")
public String index(Principal principal, Model model) {
return principal != null ? "signed-in-home" : "not-signed-in-home";
}
Read more about principal here- https://www.baeldung.com/get-user-in-spring-security
Tested in jsp + Spring 4.3.13 + Spring Security 4.2.11
1.checked in jsp
"<%# taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>"
<script>
<sec:authorize access="hasRole('ADMIN')">
location.replace('/yourpage')
</sec:authorize>
</script>
2. checked in controller
SpringSecurityUtil.class
public class SpringSecurityUtil {
public static boolean hasRole(String role) {
Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
boolean hasRole = false;
for (GrantedAuthority authority : authorities) {
if(authority.getAuthority().equals(role)) {
hasRole = true;
break;
}
}
return hasRole;
}
}
HomeController.class
#Controller
public class HomeController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpServletResponse resp) {
if(SpringSecurityUtil.hasRole("ADMIN"))
//if has role send your page
else
//if has not role send your page
}
}
3. add antMatchers in your Spring Security Config class.
antMatchers("/").hasAnyRole("ADMIN")
4. use #PreAuthority
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
HomeController.class
#Controller
public class HomeController {
#PreAuthorize("hasAuthority('ADMIN')")
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home() {
//If this code starts, you have ADMIN privileges.
}
}
see more Spring Security Method Security
Despite having the following config, accessing http://localhost:8080/rooms/rooms.json gives me a CORS error - No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have no problem to request any other path which is mapped by controller. What is the problem with static resources? How to allow cors request or exclude the resource paths without spring security?
Spring Boot 2.0.5
Spring Boot Web Starter 2.0.5
#Configuration
#EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/rooms/**")
.addResourceLocations("classpath:/rooms/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
#Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/**");
}
}
I got it working with the following configuration bean:
#Configuration
public class StaticResourcesCorsConfig
{
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
}
Note, that Spring Boot will only send the Access-Control-Allow-Origin header back on a GET request, if the Origin-header is present on the request.
Update addCorsMappings like below it could work
#Configuration
#EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/rooms/**")
.addResourceLocations("classpath:/rooms/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
#Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8080")
.allowedMethods("POST", "GET")
//.allowedHeaders("header1", "header2", "header3")
//.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
}
}
Add #CrossOrigin(value = "*") to your controller class. You can replace * with any particular URL in case to allow that origin only.
#CrossOrigin(origins = "http://localhost")
#GetMapping("/rooms/")
public Object rooms() {
// your implementation
}
You can do in this way.
https://www.viator.com/orion/nova/public/mmblite/styles-53929dcb.css
Experienced the same problem actually, but found the root cause and a solution.
Your request was most probably cached by intermediary: load balancer, CDN or caching HTTP server in front of your application as regular non-CORS request. Then you have sent request with Origin:, but the intermediary returned you the same cached response because from point of view of the cache responses by default are identified by /path + METHOD + Host:-header which were the same. To tell caches that the request with Origin: and the regular request (without Origin: need to be cached as independent entries in any cache we need Vary: Origin header in both responses. This was fixed/implemented in Spring 5.2.x (in my case it was Spring 5.1.9), in your case it was 5.0.9 (as dependency of Spring Boot 2.0.5.). Once I upgraded to Spring 5.2.0 all was fixed once caches on the intermediary had expired. I recommend to upgrade beyond 5.2.6 (cause there were further changes in CORS handling, which are nice to have).
here is the line (which made the difference) they (Pivotal) commited into Spring: https://github.com/spring-projects/spring-framework/commit/d27b5d0ab6e8b91a77e272ad57ae83c7d81d810b#r36264428
and their bug description: https://github.com/spring-projects/spring-framework/issues/22273
I am currently trying to test out Okta with SPA front end (Create-React-App) and a Spring Boot application.
Currently I have the apps working, in that a user logins on the front end (via okta). The user can then access protected resources from server (spring boot). Hence the integration works well and nice.
My issue is I can't access the Principal on my Rest Controller.
ENV
Note: Spring-Security-Starter is NOT on the classpath just the OAuth2 autoconf
Spring Boot 2.0.6.RELEASE
okta-spring-boot-starter:0.6.1
spring-security-oauth2-autoconfigure:2.0.6.RELEASE'
Spring Configuration
okta.oauth2.issuer=https://dev-886281.oktapreview.com/oauth2/default
okta.oauth2.clientId={ clientId }
okta.oauth2.audience=api://default
okta.oauth2.scopeClaim=scp
okta.oauth2.rolesClaim=groups
security.oauth2.resource.user-info-uri=https://dev-886281.oktapreview.com/oauth2/default/v1/userinfo
Okta Service Configuration
Application type: Single Page App (SPA)
Allowed grant types: Implicit
Allow Access Token with implicit grant type: true
Controller
#RestController
#RequestMapping("/products")
public class ProductController {
...
#GetMapping
public ResponseEntity<List<ProductEntity>> getAllProducts(Principal principal) {
SpringBoot
#EnableResourceServer
#SpringBootApplication
public class CartyApplication {
public static void main(String[] args) {
SpringApplication.run(CartyApplication.class, args);
}
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
#Bean
protected ResourceServerConfigurerAdapter resourceServerConfigurerAdapter() {
return new ResourceServerConfigurerAdapter() {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated();
}
};
}
Once again the overall integration is working fine, users can only access protected resources once they've signed in via okta, I'm just wondering how to get the users details from okta on the controller.
Thanks in advance.
P.S soz for the code dump
EDIT: Removed snippets and added full CartyApplication class
EDIT2: Added repo - https://github.com/Verric/carty-temp
I have a feeling you might be missing this:
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
I'm guessing should remove the .antMatchers("/**").permitAll() line.
See: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#CO3-2
I'm guessing you want to protect all/most of your endpoints? I'd recommend only allowing specific routes, and protecting everything else.
How can I obtain the x-auth-token from CORS/Spring security?
I am using Angular 4 and it is not returning the x-auth-token. Just Cache-Control, Expires, and Pragma. In the network tab in chrome it does show all my response headers, but I am not allowed to get them via Angular's response method Headers in Angular explained
.map((res: any) => {
let authToken = res.headers.get("x-auth-token");
The above will just print null if I console.log it.
I was getting a 403() at login until I added CORS configuration in spring-boot's security.
Since doing so I get a 200 in the network tab but am not allowed to get the token.
CORS config:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
// .allowedHeaders("x-auth-token")
// .exposedHeaders("x-auth-token");
}
};
}
I had to comment out:
// .allowedHeaders("x-auth-token")
// .exposedHeaders("x-auth-token");
because I would get a 403() at login once I added them so my implementation was obviously wrong.
There is a gotcha here. By default Spring CORS doesn't allow Authorization. You have had the below headers and the x-auth-token will be passed.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("x-auth-token","Authorization","Access-Control-Allow-Origin","Access-Control-Allow-Credentials")
.exposedHeaders("x-auth-token","Authorization","Access-Control-Allow-Origin","Access-Control-Allow-Credentials")
.allowCredentials(false).maxAge(3600);
}
};
}
This code blocked properly every request which is finished on XHTML but I would like redirect the request to url like "/spring/denied" not "/spring/login" which is setted on method formLogic()
http
.formLogin()
.loginPage("/spring/login")
.loginProcessingUrl("/spring/loginProcess")
.defaultSuccessUrl("/spring/main")
.failureUrl("/spring/login?login_error=1")
.and()
.logout()
.logoutUrl("/spring/logout")
.logoutSuccessUrl("/spring/logoutSuccess")
.and()
.authorizeRequests().antMatchers("/spring/**/*.xhtml").denyAll()
.and()
// Disable CSRF (won't work with JSF) but ensure last HTTP POST request is saved
// See https://jira.springsource.org/browse/SEC-2498
.csrf().disable()
.requestCache()
.requestCache(new HttpSessionRequestCache());
So, I think there will be theses possible scenes:
Someone intent to access to any real XHTML file directly (/main/index.xhtml): Behavior: Request blocked and redirected to denied url, if someone wish it , he must interact using right flow definition (p.e. /main, /groups....)
Someone intent to access to secured url without right permissions or anonymous (p.e. /admin, /authenticate...): Behavior: Spring security intercept request and redirect to login url
Some intent to access to secured url with right permissions (p.e /admin, /authenticate....): Behavior: Spring security grants access and spring web flow make its task redirecting properly
Someone intent to access unknown url (p.e. /ImAUnluckyGuyAndThisUrlIsUnreal): Behavior: Spring webflow intercept request and redirect to last flow known.
Using XML configuration above cases are right. Furthermore I used spring webflow 2.3 instead 2.4.0RC1 and Annotations configurations
Case 1: Adding this code on web.xml, I don't know how replace fot annotations configurations
<security-constraint>
<display-name>Restrict direct access to XHTML access</display-name>
<web-resource-collection>
<web-resource-name>XHTML</web-resource-name>
<url-pattern>*.xhtml</url-pattern>
</web-resource-collection>
<auth-constraint />
</security-constraint>
Case 4: Adding this code on a abstract flow definition, I don't know if doesn't work on Spring webflow 2.4.0RC1 or it's a annotations configuration problem.
<global-transitions>
<transition on-exception="org.springframework.webflow.engine.NoMatchingTransitionException" to="handlingViewState">
<evaluate expression="handlingBean.handle(flowExecutionException)"> </evaluate>
</transition>
</global-transitions>
Case 2 and 3: These are not problematic. if user is authenticated and doesn't got permissions is redirected using .exceptionHandling().accessDeniedPage("/spring/denied") or user is anonymous is redirected to loginPage()
Webflow configuration
#Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry())
.addFlowExecutionListener(new FlowFacesContextLifecycleListener())
.addFlowExecutionListener(new SecurityFlowExecutionListener())
.build();
}
#Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder(flowBuilderServices())
.setBasePath("/WEB-INF/flows")
.addFlowLocationPattern("/**/*-flow.xml")
.build();
}
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder().setDevelopmentMode(true).build();
}
}
MVC configuration
#Autowired
private WebFlowConfig webFlowConfig;
#Bean
public FlowHandlerMapping flowHandlerMapping() {
FlowHandlerMapping mapping = new FlowHandlerMapping();
mapping.setOrder(1);
mapping.setFlowRegistry(this.webFlowConfig.flowRegistry());
/* If no flow matches, map the path to a view, e.g. "/intro" maps to a view named "intro" */
mapping.setDefaultHandler(new UrlFilenameViewController());
return mapping;
}
#Bean
public FlowHandlerAdapter flowHandlerAdapter() {
JsfFlowHandlerAdapter adapter = new JsfFlowHandlerAdapter();
adapter.setFlowExecutor(this.webFlowConfig.flowExecutor());
return adapter;
}
#Bean
public UrlBasedViewResolver faceletsViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setViewClass(JsfView.class);
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".xhtml");
return resolver;
}
#Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
return new SimpleControllerHandlerAdapter();
}
What you need is multiple <http> [HttpSecurity] configuration and you need to provide a custom AuthenticationEntryPoint implementation.
Below is the HttpSecurity configuration for case 1. (I hope you can come-up with a configuration for case 2 & 3 and you can use the one you already have.)
#Configuration
#Order(1)
public static class XHTMLAccessDenyWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/spring/**/*.xhtml")
.exceptionHandling().authenticationEntryPoint(new AccessDenyEntryPoint()).and()
.authorizeRequests().antMatchers("/spring/**/*.xhtml").denyAll();
}
}
Note: The order of the above security configuration should be higher than the security configuration for case 2 & 3; therefore, #Order is used.
Custom AuthenticationEntryPoint implementation would simply redirect the request to /spring/deny page as below
public class AccessDenyEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
request.getRequestDispatcher("/spring/denied").forward(request, response);
}
}