1
votes

I have a Spring Boot Application used as a secured REST API backend. I would like to have some static page with the documentation about that API (I'd like to use a Wiki, for instance)

As far as I try I cannot make it display static contents: e.g. I tried with the greeting example and invoking http://localhost:8080/greeting it'll display "greeting" (not serving the greeting.html page)

I suspect the problem is related to some filter in Spring Security.

That's the invoked filter chain

o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 19 ms
o.s.security.web.FilterChainProxy        : /greeting at position 1 of 7 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.security.web.FilterChainProxy        : /greeting at position 2 of 7 in additional filter chain; firing Filter: 'StatelessLoginFilter'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/greeting'; against '/api/login'
o.s.security.web.FilterChainProxy        : /greeting at position 3 of 7 in additional filter chain; firing Filter: 'StatelessAuthenticationFilter'
o.s.security.web.FilterChainProxy        : /greeting at position 4 of 7 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.security.web.FilterChainProxy        : /greeting at position 5 of 7 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
o.s.security.web.FilterChainProxy        : /greeting at position 6 of 7 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
o.s.security.web.FilterChainProxy        : /greeting at position 7 of 7 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/greeting'; against '/'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/greeting'; against '/documentation'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/greeting'; against '/greeting'
o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /greeting; Attributes: [permitAll]
o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@58e65a6f, returned: 1
o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
o.s.security.web.FilterChainProxy        : /greeting reached end of additional filter chain; proceeding with original chain
o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally

I put the greeting.html file both in src/main/webapp/WEB-INF/templates and in src/main/resources/templates, I tried to specify in application.properties

# For the standard MVC JSTL view resolver
spring.view.prefix=/WEB-INF/templates/
spring.view.suffix=.html

I tried with the solutions proposed in these stackoverflow: "Spring Boot not serving static content" and "spring boot not launching static web content" but nothing changed ...

and finally this is the WebSecurityConfigurerAdapter:

public class StatelessAuthenticationSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsService userDetailsService;

@Autowired
private TokenAuthenticationService tokenAuthenticationService;

@Autowired
private LDAPAuthenticationService ldapAuthenticationService;

@Value("${ldap.useLdapForAuthentication}")
private String useLdapForAuthentication;

public StatelessAuthenticationSecurityConfig() {
    super(true);

}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .exceptionHandling().and()
            .anonymous().and()
            .servletApi().and()
            .headers().cacheControl().and()
            .authorizeRequests()

            //allow anonymous resource requests
            .antMatchers("/").permitAll()
            .antMatchers("/documentation").permitAll()
            .antMatchers("/greeting").permitAll()

            .antMatchers("/favicon.ico").permitAll()
            .antMatchers("/resources/**").permitAll()

            //allow anonymous POSTs to login
            .antMatchers(HttpMethod.OPTIONS, "/api/login").permitAll()
            .antMatchers(HttpMethod.POST, "/api/login").permitAll()
            .antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
            .antMatchers(HttpMethod.POST, "/api/**").hasAnyRole("ADMIN", "USER")
            .antMatchers(HttpMethod.GET, "/api/**").hasAnyRole("ADMIN", "USER") //e compagnia cantando 

            //defined Admin only API area
            .antMatchers("/admin/**").hasRole("ADMIN")

            //all other request need to be authenticated
            .anyRequest().hasRole("USER")
            .and()              

            // custom JSON based authentication by POST of {"username":"<name>","password":"<password>"} which sets the token header upon authentication
            .addFilterBefore(new StatelessLoginFilter("/api/login", tokenAuthenticationService, userDetailsService, ldapAuthenticationService, authenticationManager(), useLdapForAuthentication), UsernamePasswordAuthenticationFilter.class)

            // custom Token based authentication based on the header previously given to the client
            .addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class);
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}

@Override
protected UserDetailsService userDetailsService() {
    return userDetailsService;
}

}

.antMatchers("/resources/**").permitAll() - Should allow access even to resources/templates

I really cannot understand why it is not rendering the web content Please, could you give me some hint?

EDIT1

Controller:

@RestController
public class GreetingController {

@RequestMapping("/greeting")
public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
    model.addAttribute("name", name);
    return "greeting";
}
}
1
add your controller.M. Deinum

1 Answers

1
votes

According to the Spring Guide: Building a RESTful Web Service

A key difference between a traditional MVC controller and the RESTful web service controller above is the way that the HTTP response body is created. Rather than relying on a view technology to perform server-side rendering of the greeting data to HTML, this RESTful web service controller simply populates and returns a Greeting object. The object data will be written directly to the HTTP response as JSON.

So in your case it return "greetings" in JSON. If you want to let it return the page greeting.html, you should use a normal @Controller.