CrossOrigin with http security using jwt token in Spring boot - java

I have some issue regarding crossorigin and http security in my spring boot application.
I want to use http security when I annotate a method with #crossorigin in my controller class.
But it doesnt seams to work, the security is always triggered even if the method is not using #crosorigin.
Is it possible to fix this?
Jwtautoconfig class:
#ManagementContextConfiguration
#ConditionalOnProperty(name = {"af.security.active"}, havingValue = "true")
#Import({EnvironmentConfig.class, JwkRepository.class, JwtTokenUtil.class,
JwtAuthenticationProvider.class})
#EnableWebSecurity
#EnableConfigurationProperties(JwtSecurityProperties.class)
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(Ordered.HIGHEST_PRECEDENCE)
public class JwtAutoConfig extends WebSecurityConfigurerAdapter {
#Value("${af.security.jwt.white-list}")
private String[] ignoredPaths;
#Value("${af.security.job-seeker-role:arbetssökande}")
private String jobSeekerRole;
#Value("${af.security.officer-role:handläggare}")
private String officer;
#Bean(name = "jwtauthenticationentrypoint")
public JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint() {
return new JwtAuthenticationEntryPoint();
}
#Bean
public JwtSecurityHelper securityHelper(){
return new JwtSecurityHelper(jobSeekerRole, officer);
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManager());
authenticationTokenFilter.setAuthenticationSuccessHandler(new JwtAuthenticationSuccessHandler());
return authenticationTokenFilter;
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable();
// Custom JWT based security filter
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// disable page caching
http.headers().cacheControl();
}
#Override
public void configure(WebSecurity web) {
final String[] trimmedIgnoredPaths = Stream.of(ignoredPaths)
.map(String::trim)
.toArray(String[]::new);
web.ignoring()
.antMatchers(HttpMethod.OPTIONS,"/**")
.and()
.ignoring().antMatchers(trimmedIgnoredPaths);
}
private Config hazelCastConfig(){
Config config = new Config();
config.setInstanceName("app-cache")
.setNetworkConfig(new NetworkConfig()
.setJoin(new JoinConfig()
.setMulticastConfig(new MulticastConfig()
.setEnabled(false)
)
)
)
.addMapConfig(
new MapConfig()
.setName("object-cache")
.setMaxSizeConfig(new MaxSizeConfig(10, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
.setEvictionPolicy(EvictionPolicy.LRU)
.setStatisticsEnabled(true)
.setTimeToLiveSeconds(14400));
return config;
}
#Bean(name="hazelcast")
public HazelcastInstance hazelcastInstance() {
HazelcastInstance hazelcastInstance = new HazelcastInstanceFactory(hazelCastConfig()).getHazelcastInstance();
return hazelcastInstance;
}
}
CorsConfig class:
#Configuration
public class CorsConfig {
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "DELETE", "GET" )
.allowCredentials(true);
}
};
}
}
And this is the method in my controller class:
#ApiOperation(value = "Hämtar alla frånvaron för en lista med användare")
#PostMapping(path= "/hamta-alla-franvaron", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<ExternalFranvaroDTO>> hamtaAllaFranvaron(
#ApiParam(value = "Identitet objekt som innehåller en lista av PISA_ID", required = true)
#Valid #RequestBody IdentitetForm identitet){
logger.info("MOTTAGET Rest-anrop (/hamta-alla-franvaron) Hamtar alla franvaron");
List<ExternalFranvaroDTO> externalFranvaroDTOLista = new ArrayList<>();
List<Franvaro> franvaron = franvaroService.hamtaAllaPagaendeOchNyaFriskskrivnaFranvaron(identitet.getPisaIds());
if(franvaron.isEmpty()) {
logger.debug("Inga pågende sjuk/vab anmälan");
return ResponseEntity.noContent().build();
}
franvaron.forEach( franvaro -> {
ExternalFranvaroDTO externalFranvaroDTO = transformeraTillExternalFranvaroDTO(franvaro);
externalFranvaroDTOLista.add(externalFranvaroDTO);
});
return ResponseEntity.ok().body(externalFranvaroDTOLista);
}
Now I want to only use http security when I use #crossorigin

I don't see any reason as to why you would like to combine it like this.
You should instead apply security to specific endpoints and configure a cors filter in spring security instead of setting it globally as you have done.
If you read the spring security documentation under HttpSecurity you can use the antMatcher and and match endpoints using ant syntax
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatcher( // Here you can define endpoints using ant matching
"**/foo/**",
"**/bar/**"
)
.authenticated()
)
... // rest of configuration
}
You can also define a CORS filter using spring security
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors(withDefaults())
...
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
You can even activate and use the built in jwt filter and customize that filter too with your own converter etc. etc.
protected void configure(HttpSecurity http) {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}
// or add a custom converter
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
// adding a custom converter here
.jwtAuthenticationConverter(myConverter())
)
);
The spring security documentation is really, really, relly good and you should always use it first as a source of information.

Related

Multiple Spring WebSecurityConfigurerAdapter

So I have multiple Controller classes exposing a number of endpoints in my service. One of these controllers is exposing endpoints used for webhooks. I want to expose all the endpoints in the webhooks Controller class and secure them with one WebSecurityConfigurerAdapter configuration and then all the other endpoints which stretch across a few other Controller classes be configured with a separate WebSecurityConfigurerAdapter configuration. The webhooks will be accessed by third party vendors and want them using a separate auth0 audience from the other endpoints which will be accessed internally. I have it setup now as follows but it doesn't seem to be working. In order for the two configurations to co-exist they need to have an #Order annotation assigned to their configure methods and these int values must be unique. But what is happening is all requests coming in seem to go to the Order(1) configuration first but are never making it to Order(2) if they are of the pattern described in my Order(2) config. Maybe that's not how it's supposed to work or maybe my implementation is incorrect. But when I pass in a request in a "/webhooks/" endpoint it always gives me a 401 error and then when I change that configuration to Order(1) it starts to work. And this behavior happens the other way as well. When I make the "/webhooks/" patter the first order I get a 401 for all the other requests because they are not making it to the next Order. Here is my code...
#EnableWebSecurity
public class SecurityConfig {
#Configuration
public static class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${auth0.api-audience}")
private String audience;
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
#Override
#Order(1)
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// in dev if you want to bypass auth you can change /ping to /*
.mvcMatchers(HttpMethod.GET, "/actuator/**", "/test").permitAll()
.mvcMatchers("/api/**")
.authenticated()
.and()
.cors()
.configurationSource(corsConfigurationSource())
.and()
.oauth2ResourceServer()
.jwt()
.decoder(jwtDecoder(audience, issuer));
// disable cors and csrf when running locally
if (getApplicationContext().getEnvironment().acceptsProfiles(Profiles.of("local"))) {
http.cors().and().csrf().disable();
}
}
static JwtDecoder jwtDecoder(String audience, String issuer) {
OAuth2TokenValidator<Jwt> withAudience = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withBrand = new BrandValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>(withAudience, withBrand, withIssuer);
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuer);
jwtDecoder.setJwtValidator(validator);
return jwtDecoder;
}
}
#Configuration
#Order(2)
public static class WebhookSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${auth0.webhook-audience}")
private String audience;
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/webhooks/**")
.authenticated()
.and()
.cors()
.configurationSource(corsConfigurationSource())
.and()
.oauth2ResourceServer()
.jwt()
.decoder(jwtDecoder(audience, issuer));
// disable cors and csrf when running locally
if (getApplicationContext().getEnvironment().acceptsProfiles(Profiles.of("local"))) {
http.cors().and().csrf().disable();
}
}
JwtDecoder jwtDecoder(String audience, String issuer) {
OAuth2TokenValidator<Jwt> withAudience = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>(withAudience, withIssuer);
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuer);
jwtDecoder.setJwtValidator(validator);
return jwtDecoder;
}
}
static CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedMethods(List.of(
HttpMethod.GET.name(),
HttpMethod.PUT.name(),
HttpMethod.POST.name(),
HttpMethod.DELETE.name()
));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration.applyPermitDefaultValues());
return source;
}
}
In addition to the behavior I am seeing I am also seeing on startup of the Spring application...
2022-04-18 16:42:00,616 INFO org.springframework.security.web.DefaultSecurityFilterChain : Will not secure any request
2022-04-18 16:42:00,618 INFO org.springframework.security.web.DefaultSecurityFilterChain : Will not secure any request

Spring Boot register with JWT

I am currently working on a Spring project, where I got the Security already implemented.(University) But I occurred the following problem: If I want to register a new user to the system, I logically do not have a JWT to authenticate the new user. I just get Invalid authorization header or token back from Spring, if I try to register a new user. So, I think the Security is not right configured:
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
private final PasswordEncoder passwordEncoder;
private final RequestMatcher whiteListedRequests;
private final SecurityProperties securityProperties;
private final JwtTokenizer jwtTokenizer;
#Autowired
public SecurityConfig(UserService userService,
PasswordEncoder passwordEncoder,
SecurityProperties securityProperties, JwtTokenizer jwtTokenizer) {
this.userService = userService;
this.securityProperties = securityProperties;
this.passwordEncoder = passwordEncoder;
this.jwtTokenizer = jwtTokenizer;
this.whiteListedRequests = new OrRequestMatcher(securityProperties.getWhiteList().stream()
.map(AntPathRequestMatcher::new)
.collect(Collectors.toList()));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.csrf()
.disable();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/v1/users/sign-up")
.permitAll();
http.authorizeRequests().anyRequest()
.authenticated();
http.addFilter(new JwtAuthenticationFilter(authenticationManager(), securityProperties, jwtTokenizer));
http.addFilter(new JwtAuthorizationFilter(authenticationManager(), securityProperties));
}
#Override
public void configure(WebSecurity web) {
web.ignoring().requestMatchers(whiteListedRequests);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final List<String> permitAll = Collections.unmodifiableList(Collections.singletonList("*"));
final List<String> permitMethods = List.of(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.PUT.name(),
HttpMethod.PATCH.name(), HttpMethod.DELETE.name(), HttpMethod.OPTIONS.name(), HttpMethod.HEAD.name(),
HttpMethod.TRACE.name());
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedHeaders(permitAll);
configuration.setAllowedOrigins(permitAll);
configuration.setAllowedMethods(permitMethods);
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Does anyone has a tip, how I can change the securityconfig, that a user can register himself against my Endpoint? (/api/v1/users/sign-up) I am kind of lost at this, and trying for several hours to manage the wanted behavior!
Thanks in Advance!
EDIT:
I just figured that my application.yml holds a whitelist:
security:
auth:
header: Authorization
prefix: "Bearer "
login-uri: /api/v1/authentication
white-list:
# Browser requests
- /
- /favicon.ico
- /csrf
- /v2/api-docs
- /swagger-resources/**
- /webjars/**
- /swagger-ui.html
# H2
- /h2-console/**
# Registration
- /api/v1/registrations/
If I add the URI here, it works properly. But is there a solution to just add it via Code?
the registration uri must not request a token, it must be allowed access from the outside.
it must be enabled at the antMatcher
Here is an exemple
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/regitration/**")
.permitAll();
http.authorizeRequests().anyRequest()
.authenticated();
http.addFilter(new JWTAuthenticationFilter(authenticationManager(),userRepository,iLdapService,cryptoHelper));
http.addFilterBefore(new JWTAutorizationFilter(),UsernamePasswordAuthenticationFilter.class);
}

Configure swagger with JWT

I want install SWAGER for my SpringBoot application. Seems that JWT doesn't give access for swagger URL.
I'm trying to reach this by url localhost:8088/swagger-ui.html
Here is SwaggerConfig class
#EnableSwagger2
#Configuration
public class SwaggerConfig {
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("Path.to.my.controller"))
.build();
}
}
Also i was trying to add WebAppConfig from link with the next content
#Configuration
#EnableWebMvc
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
And tried to set ignore url:
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
}
This version of code gives autoredirection to "localhost:8088/login" from swagger url. But the next returns just empty page
UPDATED
web.ignoring().antMatchers("/", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**");
The urls in gaps are urls i was seen when was debuging issuse. This urls are called by swagger.
UPDATED part End
Main class
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));
SpringApplication app = new SpringApplication(Application.class);
app.run();
}
#Bean
#Autowired
public FilterRegistrationBean jwtFilterRegistration(JwtUtil jwtUtil, UserService userService) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new JwtFilter(jwtUtil, userService));
filterRegistrationBean.addUrlPatterns("/*");
// ordering in the filter chain
filterRegistrationBean.setOrder(1);
return filterRegistrationBean;
}
// Request Interceptor for checking permission with custom annotation.
#Bean
public MappedInterceptor PermissionHandlerInterceptor() {
return new MappedInterceptor(null, new PermissionHandlerInterceptor());
}
}
Pom xml contains all needed depencies. When i comment in Main class jwt method i can access swagger. So i made a conclusion that problem in JWT.
If some extra info is needed i will add.
UPDATED
At first swagger-url gives White Label Page with an error "Unathorized"
After some manipulations with code it gives empty page.
I recently had to do the same. You need to tell your Spring Security to permit all Swagger resources. Try this:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// allow anonymous resource requests
.antMatchers(
HttpMethod.GET,
"/",
"/v2/api-docs", // swagger
"/webjars/**", // swagger-ui webjars
"/swagger-resources/**", // swagger-ui resources
"/configuration/**", // swagger configuration
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilter,
UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
This is my Swagger docket configuration. It also include the Authorization Header in case you want to apply you token to all endpoint.
#Bean
public Docket newsApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.securitySchemes(Lists.newArrayList(apiKey()))
.securityContexts(Lists.newArrayList(securityContext()))
.apiInfo(generateApiInfo());
}
#Bean
SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.any())
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(
new SecurityReference("JWT", authorizationScopes));
}
private ApiKey apiKey() {
return new ApiKey("JWT", "Authorization", "header");
}

Spring security 401 Unauthorized on unsecured endpoint

I'm trying to configure Spring Security on a Spring Boot application as follows:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
#Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationFilter authenticationTokenFilter = new JwtAuthenticationFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//#formatter:off
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(this.unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login", "/singup", "/subscribers").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
//#formatter:on
}
}
My unauthorizedHandler is:
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger LOGGER = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
Finally, the REST controller for /subscribers is:
#RestController
public class SubscriberRestController {
#Autowired
ISubscribersService subscribersService;
#RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.GET)
#ResponseBody
public Number subscriberCount() {
return subscribersService.subscribersCount();
}
#RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.POST)
public String subscriberPost(#RequestBody SubscriberDocument subscriberDocument) {
return subscribersService.subscribersInsert(subscriberDocument);
}
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return "This is a test";
}
}
I use postman to test endpoints and when I do a POST to "localhost:8080/subscribers", I get:
I want to have opened endpoints (/subscribers) without any security control or credentials check, endpoints for singup and login and secured endpoints for authenticated users.
Thanks! :)
Spring Boot was not applying the configuration because couldn't find it. On Application.java config package was not included with #ComponentScan anotation.
After some researching, here is solution:
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
#ComponentScan(basePackages = { PackageConstants.PACKAGE_CONTROLLERS_REST, PackageConstants.PACKAGE_SERVICES,
PackageConstants.PACKAGE_SERVICES_IMPL, PackageConstants.PACKAGE_MONGO_REPOSITORIES,
PackageConstants.PACKAGE_MONGO_REPOSITORIES_IMPL, PackageConstants.PACKAGE_UTILS })
public class Application {
// Clase principal que se ejecuta en el bootrun
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Main line is #SpringBootApplication(exclude = {SecurityAutoConfiguration.class }) it tells not use Spring Boot Security AutoConfiguration configuration. It is not full answer, because now you have to tell Spring user your Spring Security configuration class. Also i advice you to create Initializer class with init Root Config Classes, ApplicationConfiguration using and refuse to use SpringBoot applications. Something like this:
ApplicationConfig:
#Configuration
#EnableWebMvc
#ComponentScan("com.trueport.*")
#PropertySource("classpath:app.properties")
public class ApplicationConfig extends WebMvcConfigurerAdapter {
....
}
ApplicationSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
....
}
Initializer:
public class Initializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
....
DispatcherServlet dispatcherServlet = new DispatcherServlet(ctx);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ctx.register(ApplicationConfig.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME,
dispatcherServlet);
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);
}
}
You need to add the following to your configure method /error is the default fall back when error occurs to the application due to any exception and it is secured by default.
protected void configure(HttpSecurity httpSecurity) throws Exception {
//disable CRSF
httpSecurity
//no authentication needed for these context paths
.authorizeRequests()
.antMatchers("/error").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/your Urls that dosen't need security/**").permitAll()
Also the below code snippet
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/error/**")
}
Now you will not get 401 and get 500 exception with details when an exception occurred for permitAll Urls
If your application is simply saving APIs, and you have included dependency for spring security - for any other reason (Mine was to enables headers X-Frame and Content-Security-Policy), then by default Spring includes servlet filter for csrf protection. If you do not disable this, all requests fail with HTTP 401 error.
To disable it, You create a Configuration class extending WebSecurityConfigurerAdapter and annotated with EnableWebSecurity
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
}
This article is worthy the read - very detailed.

How do I configure Spring Security CAS support using Java configuration?

I'm trying to setup CAS authentication using Spring Security for my web application. I've followed the documentation and managed to convert the XML configuration examples to Java config. However, I'm not sure I did everything correctly and given the sensitiveness of security, I'd like someone to confirm that there are no mistakes.
For example, how can I be sure there are not default configurations anymore (like liberal permissions on URLs, different authentication managers and/or providers, etc...)?
Is the way I retrieved the current AuthenticationManager correct?
Is configuring the EntryPoint like I did the correct way?
I find understanding how to use WebSecurityConfigurerAdapter rather confusing...
This is my #Cofiguration class:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean(name="authenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
// TODO Auto-generated method stub
return super.authenticationManagerBean();
}
#Bean
public ServiceProperties serviceProperties() {
final ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService("http://localhost:8088/webapp/login/cas");
return serviceProperties;
}
#Bean
public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService() {
return new MyCasAssertionUserDetailsService();
}
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
final CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setAuthenticationUserDetailsService(authenticationUserDetailsService());
casAuthenticationProvider.setTicketValidator(new Cas20ProxyTicketValidator("https://my.cas.server.com/cas"));
casAuthenticationProvider.setKey("MY-KEY");
auth.authenticationProvider(casAuthenticationProvider);
}
#Bean
public CasAuthenticationEntryPoint casEntryPoint() {
final CasAuthenticationEntryPoint casEntryPoint = new CasAuthenticationEntryPoint();
casEntryPoint.setServiceProperties(serviceProperties());
casEntryPoint.setLoginUrl("https://my.cas.server.com/cas/activateAndLogin");
return casEntryPoint;
}
// filter to invoke the CAS server when the user click on "Logout from CAS" in the local logout success page
#Bean
public LogoutFilter requestSSOLogoutToCASServerLogoutFilter() {
final LogoutFilter logoutFilter = new LogoutFilter("https://my.cas.server.com/cas/logout", new SecurityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl("/logout/cas");
return logoutFilter;
}
// filter that receives the request to logout from the CAS server
#Bean
public SingleSignOutFilter singleSignOutFilter() {
return new org.jasig.cas.client.session.SingleSignOutFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
final CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
http
.exceptionHandling().authenticationEntryPoint(casEntryPoint())
.and()
.logout()
.logoutSuccessUrl("/cas-logout") // which page to redirect the User after the local log-out succeeded
.permitAll() // all users can logout
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(casAuthenticationFilter)
.addFilterBefore(requestSSOLogoutToCASServerLogoutFilter(), LogoutFilter.class)
.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class)
;
}
}

Categories