I'm trying to configure spring security and controller CORS requests, but the preflight request is not working. I have a controller, test, and spring security config which are related to the problem. What I'm doing wrong?)
Java 8, Spring Boot 2.1.4
Controller
#RestController
#RequestMapping("/login")
#CrossOrigin
public class LoginController {
#RequestMapping(path = "/admin", consumes = "application/json", produces = "application/json")
public ResponseEntity<Admin> loginAdmin(#RequestBody Admin admin) {
String username = admin.getUsername();
String password = admin.getPassword();
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
ResponseEntity<Admin> responseEntity;
Admin foundAdmin = adminRepository.findByUsername(username);
if (foundAdmin != null) {
String token = jwtTokenProvider.createToken(username, adminRepository.findByUsername(username).getRoles());
AdminAuthenticatedResponse contractorAuthenticatedResponse = new AdminAuthenticatedResponse(foundAdmin, token);
responseEntity = new ResponseEntity<>(contractorAuthenticatedResponse, HttpStatus.ACCEPTED);
} else {
responseEntity = new ResponseEntity<>(admin, HttpStatus.NOT_FOUND);
}
return responseEntity;
}
}
Security config
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.cors().and().formLogin().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/login/**").permitAll()
.antMatchers("/register/**").permitAll()
.antMatchers("/h2-console/**").permitAll()
.antMatchers(HttpMethod.GET, "/restapi/customers/**").permitAll()
.antMatchers(HttpMethod.DELETE, "/restapi/customers/**").hasRole("CUSTOMER")
.antMatchers(HttpMethod.PUT, "/restapi/customers/**").hasRole("CUSTOMER")
.antMatchers(HttpMethod.PATCH, "/restapi/customers/**").hasRole("CUSTOMER")
.antMatchers(HttpMethod.GET, "/restapi/contractors/**").permitAll()
.antMatchers(HttpMethod.DELETE, "/restapi/contractors/**").hasRole("CONTRACTOR")
.antMatchers(HttpMethod.PUT, "/restapi/contractors/**").hasRole("CONTRACTOR")
.antMatchers(HttpMethod.PATCH, "/restapi/contractors/**").hasRole("CONTRACTOR")
.antMatchers(HttpMethod.PATCH, "/restapi/workRequests/**").hasAnyRole("CONTRACTOR", "CONTRACTOR", "ADMIN")
.antMatchers(HttpMethod.PUT, "/restapi/workRequests/**").hasAnyRole("CONTRACTOR", "CONTRACTOR", "ADMIN")
.antMatchers(HttpMethod.POST, "/restapi/workRequests/**").hasAnyRole("CONTRACTOR", "CONTRACTOR", "ADMIN")
.antMatchers(HttpMethod.DELETE, "/restapi/workRequests/**").hasAnyRole("CONTRACTOR", "CONTRACTOR", "ADMIN")
.antMatchers(HttpMethod.GET, "/restapi/workRequests/**").hasAnyRole("CONTRACTOR", "CONTRACTOR", "ADMIN")
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider))
// Allow pages to be loaded in frames from the same origin; needed for H2-Console
.and()
.headers()
.frameOptions()
.sameOrigin();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userRepositoryUserDetailsService)
.passwordEncoder(encoder());
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(ALL));
configuration.setAllowedMethods(Arrays.asList(ALL));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/**", "/resources/**", "/index.html", "/login/admin", "/template/**", "/",
"/error/**", "/h2-console", "*/h2-console/*");
}
Test
#Test
public void testLogin() {
Admin admin = new Admin("network", "network");
// adminRepository.save(admin);
String s = adminRepository.findByUsername("network").getUsername();
RequestEntity<Admin> requestEntity =
RequestEntity
.post(uri("/login/admin"))
.contentType(MediaType.APPLICATION_JSON)
.body(admin);
ResponseEntity<Admin> responseEntity = this.restTemplate.exchange(requestEntity, Admin.class);
assertNotNull(responseEntity);
System.out.println(responseEntity.getStatusCode());
}
I'm expecting successful request, but I'm getting INTERNAL SERVER ERROR.
403 Forbidden or No Permission to Access.
A 403 Forbidden error means that you do not have permission to view the requested file or resource
Please attempt the test runs with #WithMockUser
#Test
#WithMockUser
public void corsWithAnnotation() throws Exception {
ResponseEntity<Admin> entity = this.restTemplate.exchange(
...
}
Reference : Preview Spring Security Test: Web Security
Related
I'm facing what I think is an ordering issue of my extended WebSecurityConfigurationAdapter which I would be very grateful if someone could take a look / offer some advice.
To give some context. I'm working on a API which has a fair amount of endpoints. Up until this point all endpoints where secured behind a JWT / Authentication object like so:
#GetMapping("/me")
public User getLoggedInUsersProfile(#ApiIgnore Authentication authentication,
#RequestParam(value = "profileView", required = false) String profileView) {
logger.info("Request received from User with Id {} to retrieve their profile", authentication.getName());
return userService.getUserProfileFromDB(authentication.getName());
}
However now I need to have a single endpoint which can be accessed by anyone without a JWT.
I tried adding a antMatcher and permit all for said endpoint BUT it giving me a 401. Now I did manage to get it somewhat working however then the endpoint in the code snippet above would throw a 500 if a JWT was omitted from the request (due to it authentication being null). I don't really want to add a null check for each of the endpoints as there are a LOT.
Here is my security config:
#Configuration
#EnableWebSecurity(debug = false)
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${auth0.audience}")
private String audience;
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3010"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.setAllowCredentials(true);
configuration.addAllowedHeader("Authorization");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.headers().referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN)
.and()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'").and()
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.anonymous().and()
.authorizeRequests()
.antMatchers("/api/v1/admin/**").permitAll().and()
.authorizeRequests().anyRequest()
.authenticated()
.and()
.oauth2ResourceServer().jwt();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/health", "/health/**");
}
#Bean
JwtDecoder jwtDecoder() {
/*
By default, Spring Security does not validate the "aud" claim of the token, to ensure that this token is
indeed intended for our app. Adding our own validator is easy to do:
*/
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
JwtDecoders.fromOidcIssuerLocation(issuer);
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
}
JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("permissions");
converter.setAuthorityPrefix("");
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
}
Can anyone please give me some advice / spot whats wrong.
Many thanks in advance
** EDIT **
I currently handle when a user does not supply a JWT with this class:
#Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Value("${error.unauthorized}")
private String unauthorizedErrorCode;
#Value("${api.version}")
private String currentApiVersion;
private static final Logger logger = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
private final Logger LOGGER = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
// This is invoked when user tries to access a secured REST resource without supplying any credentials
#Override
public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
final AppError error = new AppError(
currentApiVersion,
unauthorizedErrorCode,
"Access Denied",
httpServletRequest.getRequestURI(),
"Invalid or Missing Token",
e.getMessage(),
"https://xxxxxx.ai/sendreport?"
);
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpServletResponse.setContentType("application/json");
MDC.put("api.version", httpServletRequest.getContextPath());
MDC.put("Server.IP", httpServletRequest.getServerName());
MDC.put("API.Controller", httpServletRequest.getServletPath());
MDC.put("Response.code", String.valueOf(httpServletResponse.getStatus()));
MDC.put("Request.Method.Type", httpServletRequest.getMethod());
LOGGER.info("statusCode {}, path: {}, method: {}, query {}, context {}, serverName {}, RequestURI {}, RemoteHost {}, Cookies {}",
httpServletResponse.getStatus(), httpServletRequest.getRequestURI(), httpServletRequest.getMethod(),
httpServletRequest.getQueryString(), httpServletRequest.getContextPath(),
httpServletRequest.getServerName(), httpServletRequest.getRequestURI(), httpServletRequest.getRemoteAddr(),
httpServletRequest.getCookies());
MDC.clear();
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(httpServletResponse.getOutputStream(), error);
}
}
I have a problem with CORS, but only at some versions of Firefox and Safari: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ... (Reason: CORS request did not succeed). At Chrome it's fine for all testing machines. Here's my configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableAutoConfiguration
public class ApplicationConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/some_public_urls")
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
TokenAuthenticationProvider provider;
public ApplicationConfig(final TokenAuthenticationProvider provider) {
super();
this.provider = requireNonNull(provider);
}
#Autowired
private Environment env;
#Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
#Override
public void configure(final WebSecurity web) {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.headers()
.and()
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
.and()
.authenticationProvider(provider)
.addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS)
.authenticated()
.and()
.cors()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
TokenAuthenticationFilter restAuthenticationFilter() throws Exception {
final TokenAuthenticationFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
#Bean
SimpleUrlAuthenticationSuccessHandler successHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy((httpServletRequest, httpServletResponse, s) -> {
// No redirect is required
});
return successHandler;
}
/**
* Disable Spring boot automatic filter registration.
*/
#Bean
FilterRegistrationBean disableAutoRegistration(final TokenAuthenticationFilter filter) {
final FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
#Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new HttpStatusEntryPoint(FORBIDDEN);
}
}
Each RestController is annotated with:
#RestController
#CrossOrigin
I could guess that you are performing testing on some old browsers and it doesn't work.
Here is the landscape of CORS support in browsers. Please check it out.
As of mid-2014, approximately 83% of the browsers out there have full
support for CORS, and another 6% have partial support.
If it's the case, you could try some other techniques like
JSON-P or using Proxy Server to make cross-origin requests in older browser.
Ok,
It turned out, that it was certificate issue. We have a certificate bundle (wildcard certificate) and it was placed in wrong order. Some browsers could handle this, some versions of Firefox were blocking it.
I'm trying to add maximum sessions to my spring security context, however the settings are not taking effect. I've been following this code to implement JWT tokens on a spring backend using this tutorial.
I've added the relevant code I believe to the security config:
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
JwtAuthenticationFilter filter = new JwtAuthenticationFilter();
return filter;
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.authorizeRequests()
.antMatchers("/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability")
.permitAll()
.antMatchers(HttpMethod.GET, "/api/polls/**", "/api/users/**")
.permitAll()
.antMatchers("/api/configuration/**")
.permitAll()
.anyRequest()
.authenticated()
.and().sessionManagement()
.maximumSessions(1)
.sessionRegistry(sessionRegistry());
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
However when I try and login with a user - the user can login however many times they wish.
I tried looking in the user registry to see if there were multiple user principles being created for each login but there isn't.
I also tried to step through ConcurrentSessionControlAuthenticationStrategy.onAuthentication however that doesn't seem to be being called - but I have no idea why.
Edit:
Adding the customer AuthenticationEntryPoint:
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LogManager.getLogger(AuthController.class);
#Override
public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
log.error("Responding with unauthorized error. Message - {}", e.getMessage());
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Sorry, You're not authorized to access this resource.");
}
}
EDIT:
The session is setting set correctly in the session registry now. However because my login endpoint has permitAll() (I think)
ConcurrentSessionControlAuthenticationStrategy.onAuthentication() is not being called and adding the newly created session to the registry.
I believe it might be something to do with the way my CustomAuthenticationProvider is setup:
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsernameOrEmail(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
So signin is always allowing new logins as ConcurrentSessionControlAuthenticationStrategy.onAuthentication() is not being called after authentication.
I'm trying to restrict access to a page called dashboard.html to unauthenticated users. So far, I've had no success. Here's my WebSecurityConfigurerAdapter:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private CustomAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private TokenAuthenticationService tokenAuthenticationService;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/index.html", "/",
"/login.html","/signup.html", "/videos/**",
"/login", "/logout", "/images/**", "/fonts/**",
"/css/**", "/js/**", "/pages/**", "/sass/**"
).permitAll()
.and()
.authorizeRequests()
.antMatchers("/dashboard/**", "/dashboard.html/**").authenticated()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(new StatelessLoginFilter("/login", tokenAuthenticationService, userDetailsService, authenticationManager()), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class)
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.usernameParameter("email")
.passwordParameter("password")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.and()
.logout()
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterAfter(new CsrfTokenFilter(), CsrfFilter.class);
}
}
Whenever I have it set up this way, whenever I try to login, an infinite redirect loop is caused. The browser tries to navigate to dashboard.html but is restricted. This causes a redirect to the login page, which tries to redirect to the dashboard since there is a valid token.
If I have it set up like below, everyone can access dashboard.html and make calls to the /dashboard endpoint which is not desired:
http
.authorizeRequests()
.antMatchers("/index.html", "/",
"/login.html","/signup.html", "/videos/**",
"/login", "/logout", "/images/**", "/fonts/**",
"/css/**", "/js/**", "/pages/**", "/sass/**",
"/dashboard/**", "/dashboard.html/**").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
My login uses JWT tokens and uses the filter below to set the SecurityContext placeholder:
class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {
private final TokenAuthenticationService tokenAuthenticationService;
private final CustomUserDetailsService userDetailsService;
protected StatelessLoginFilter(String urlMapping, TokenAuthenticationService tokenAuthenticationService,
CustomUserDetailsService userDetailsService, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(urlMapping));
this.userDetailsService = userDetailsService;
this.tokenAuthenticationService = tokenAuthenticationService;
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
final BusinessUser user = new ObjectMapper().readValue(request.getInputStream(), BusinessUser.class);
final UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken(
user.getEmail(), user.getPassword());
return getAuthenticationManager().authenticate(loginToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authentication) throws IOException, ServletException {
final BusinessUser authenticatedUser = userDetailsService.loadUserByUsername(authentication.getName());
final UserAuthentication userAuthentication = new UserAuthentication(authenticatedUser);
tokenAuthenticationService.addAuthentication(response, userAuthentication);
SecurityContextHolder.getContext().setAuthentication(userAuthentication);
}
I'm using the line SecurityContextHolder.getContext().setAuthentication(userAuthentication); to set the authentication. This works perfectly fine. If a user is found in the DB matching the credentials sent from the user, then the security context is usable to retrieve various data associated to the user.
MY QUESTION: How can I restrict the page dashboard.html and calls to the /dashboard endpoint to unauthenticated users (those without an authentication object inside the SecurityContextHolder)?
You can use a custom RequestMatcher in combination with denyAll. First, your custom matcher:
public class PermittedPagesMatcher implements RequestMatcher {
#Override
public boolean matches(HttpServletRequest httpServletRequest) {
if (matchesToPaths(httpServletRequest,"/index.html", "/", "/login.html","/signup.html", "/videos/**", "/login", "/logout", "/images/**", "/fonts/**", "/css/**", "/js/**", "/pages/**", "/sass/**", "/dashboard/**", "/dashboard.html/**")) {
return true;
}
if (matchesToPaths(httpServletRequest, "/dashboard/**", "/dashboard.html/**")) {
return httpServletRequest.getUserPrincipal() == null;
}
return false;
}
private boolean matchesToPaths(HttpServletRequest httpServletRequest, String... paths) {
for (String p : paths) {
if (new AntPathRequestMatcher(p).matches(httpServletRequest)) {
return true;
}
}
return false;
}
}
This custom RequestMatcher filters your request to permitted pages to all of your default pages and the dashboard is only available if the request is not authenticated.
Second, combine the matcher and denyAll()
http
.authorizeRequests()
.requestMatchers(new PermittedPagesMatcher())
.permitAll()
.and()
.antMatchers("/dashboard/**", "/dashboard.html/**")
.denyAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
denyAll() ensures, that by default no one is allowed to access this page.
Attention: The order of permit and deny is important!
I have a Spring Boot application, in which most of the pages are secured (you need to log in to access them) using following security configuration.
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
class SecurityConfiguration extends
WebSecurityConfigurerAdapter {
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("myapp")
.roles("ADMIN")
.and()
.withUser("guest")
.password("guest")
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/index.html", "/home.html", "/login.html", "/")
.permitAll()
.anyRequest().authenticated().and()
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
private CsrfTokenRepository csrfTokenRepository() {
final HttpSessionCsrfTokenRepository repository =
new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
In my application class, I have a service publicData, which I want to be accessible without authentication (even, if the user isn't logged in).
#SpringBootApplication
#RestController
public class MyAppApplication {
#RequestMapping("/resource")
public Map<String,Object> home() {
final Map<String,Object> model = new HashMap<>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
#RequestMapping("/publicData")
public String publicData() {
return ...;
}
#RequestMapping("/user")
public Principal user(final Principal user) {
return user;
}
public static void main(final String[] args) {
SpringApplication.run(MyAppApplication.class, args);
}
}
How can I do this?
I tried
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/index.html", "/home.html", "/login.html", "/")
.permitAll()
.anyRequest().authenticated().and()
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.and()
.authorizeRequests()
.antMatchers("/publicData").permitAll();
}
but it didn't work.
You can create a role with all permission to access and grant that access just in some methods using Spring security annotation
http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
Of course every user need to get this role automatically when he connect in your application.
<http use-expressions="true">
<intercept-url pattern="/*"
access="hasRole('admin')"/>
</http>
Then in your free access method
#PreAuthorize("hasRole('admin')")
public void create(Contact contact);