I am following this guide for spring internationalization it implements LocalResolver like this
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.US);
return sessionLocaleResolver;
}
but I want to set defaultLocal by getting user language information in database and set it how can I do that? thanks for help
I think you want to set the Locale for the current session, not the default Locale.
Assuming there is an existing session (i.e. after user login):
Autowire LocaleResolver, HttpServletRequest and HttpServletResponse and use the LocaleResolver.setLocale method:
Locale userLocale = getLocaleByUsername(username); //load from DB
localeResolver.setLocale(httpServletRequest, httpServletResponse, userLocale);
This will set the Locale for the current session.
One standard approach you can try is using HttpHeaders.ACCEPT_LANGUAGE header. I assume you are storing supported locales in DB , so for conviniece move it to properties file , as number of records wont be many. Then try my approach like this
public Locale resolveLocale(HttpServletRequest request) {
String header = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
List<Locale.LanguageRange> ranges = Locale.LanguageRange.parse(header);
return findMatchingLocale(ranges);
}
public Locale findMatchingLocale(List<Locale.LanguageRange> lang) {
Locale best = Locale.lookup(lang, supportedLocale); // you can get supported from properties file , we maintaining list of supported locale in properties file
String country = findCountry(lang);
return new Locale(best.getLanguage(), country);
}
public String findCountry(List<Locale.LanguageRange> ranges) {
Locale first = Locale.forLanguageTag(ranges.get(0).getRange());
first.getISO3Country();
return first.getCountry();
}
If you use spring security ,maybe this solution help you.
Internationalization config :
#Configuration
#EnableAutoConfiguration
public class InternationalizationConfig extends WebMvcConfigurerAdapter {
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(new Locale("tr"));//Locale.forLanguageTag("tr"));//
// slr.setDefaultLocale(Locale.forLanguageTag("tr"));
return slr;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
Spring Security config:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedPage("/login")
.and()
.formLogin().loginPage("/index")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/j_spring_security_check")
.failureUrl("/loginFail")
.defaultSuccessUrl("/loginSuccess")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/index")
;
http.headers().frameOptions().disable();
}
}
Controller :
#Controller
public class LoginController {
#RequestMapping("/loginSuccess")
public String loginSuccess(){
User user = getUserFromDatabase;
return "redirect:/home?lang="+user.getLanguage();
}
}
Related
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.
I'm using Spring Security 4.0.3 with a custom login form and Spring MVC 4.1.1, running in Glassfish 4.1.
My custom login page is presented correctly when I ask for a secured URL (/app/**). However, when the login form is submitted (POST /j_security_check), this request results in a basic authentication dialog being displayed. It seems that something feels that /j_security_check is a resource which is protected by basic authentication at the Glassfish level, since I get a 401 Unauthorized page from Glassfish when I hit Cancel on the dialog.
Here is my MVC initializer:
public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { MvcConfiguration.class, SecurityConfiguration.class, PersistenceConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "*.html", "/j_security_check" };
}
}
My MVC Config:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.elemenopy.wishlist"})
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/*
* Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
*/
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasenames("messages", "validationMessages");
return source;
}
#Bean
#Override
public Validator getValidator() {
LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
factory.setValidationMessageSource(messageSource());
return factory;
}
#Bean
public MessageSourceAccessor messageSourceAccessor() {
return new MessageSourceAccessor(messageSource());
}
}
My security config:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("bill#example.com").password("abc123").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/static/**", "/*.jsp").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/home.html").permitAll()
.antMatchers("/start.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login.html").permitAll()
.and()
.logout().logoutUrl("/logout.html").permitAll();
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationManager authManager() throws Exception {
return authenticationManagerBean();
}
}
Any help would be greatly appreciated. Thanks!
Don't know why I didn't find this while I was searching before, but it turns out with Spring Security 4, your login form post URI has to be consistent with the URI of the login page itself. Not a hardcoded URI like /j_security_check.
spring security 4 custom login page
I am wondering how can I make my "redirect:" workin with Spring Security.
All /auth* pathes work correctly. But when it cames up to [1] it just doesn't redirect. Spring Security 4.0.2.RELEASE, Spring MVC 4.0.8.RELEASE
#Controller
#RequestMapping(value = "/auth")
public class SomeAuthController {
#RequestMapping(value = "/external")
public String externalAuth(...) {
if(someCondition) return "redirect:" + someExternalUrl; // [1] https://external-service.com
else return "redirect:/"
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth,
ShaPasswordEncoder shaPasswordEncoder,
List<AuthenticationProvider> authProviders)
throws Exception {
for(AuthenticationProvider provider : authProviders) auth.authenticationProvider(provider);
}
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/resources/**").permitAll();
http.authorizeRequests().antMatchers("/auth/**", "/").permitAll().anyRequest().authenticated();
http.formLogin()
.loginPage("/auth/login")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username")
.passwordParameter("j_password")
.failureUrl("/auth/login?error")
.permitAll();
http.logout()
.permitAll()
.logoutUrl("/auth/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true);
}
}
Okay guys. Here's my answer. Hope it will help someone.
The first thing is to enable JSR250 in the security configuration bean.
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
Afterwards I added #PermitAll annotation for a method which contained redirecting.
#PermitAll
#RequestMapping(value = "/external")
public String externalAuth(...) {
if(someCondition) return "redirect:" + someExternalUrl; // [1] https://external-service.com
else return "redirect:/"
}
That's all. Have a nice debugging J
I'm trying to handle 404 error using an #ControllerAdvice in a Spring MVC application totally configured using Javaconfig.
Spring MVC version is 4.1.5
I have read this:
Stackoverflow Example 1
Stackoverflow Example 1 continuation
But unfortunately it does not work for me.
Here you have my conf:
SpringConfigurationInitializer
public class SpringConfigurationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
}
Note that i'm using
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
And
GlobalExceptionHandler (version 1)
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(NoHandlerFoundException.class)
public ModelAndView handleError404(HttpServletRequest request, Exception e) {
System.out.println("handled!!!");
ModelAndView mav = new ModelAndView("/errors/404");
mav.addObject("exception", e);
return mav;
}
}
GlobalExceptionHandler (version 2)
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#Override
public ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
System.out.println("handled¡¡¡");
return null;
}
}
Keep in mind that i'm not using any kind of xml config file and i'm trying to build a web application (not REST)
AppConfiguration
#Configuration
#ComponentScan({ "org.moyanojv.opendata.*" })
#Import({ MvcConfiguration.class, RepositoryConfiguration.class, SecurityConfig.class })
public class AppConfiguration extends WebMvcConfigurerAdapter{
}
MvcConfiguration
#EnableWebMvc
#Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Bean
public UrlBasedViewResolver viewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
#Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer tilesConfigurer = new TilesConfigurer();
tilesConfigurer.setDefinitions(new String[] { "/WEB-INF/tiles.xml" });
tilesConfigurer.setCheckRefresh(true);
return tilesConfigurer;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/* Localization section is started */
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor localeChangeInterceptor=new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
#Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver(){
return new CookieLocaleResolver();
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("i18n/messages");
source.setUseCodeAsDefaultMessage(true);
return source;
}
}
RepositoryConfiguration
#Configuration
public class RepositoryConfiguration {
}
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");
}
#Override
public void configure( WebSecurity web ) throws Exception
{
// This is here to ensure that the static content (JavaScript, CSS, etc)
// is accessible from the login page without authentication
web
.ignoring()
.antMatchers( "/resources/**" );
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// access-denied-page: this is the page users will be
// redirected to when they try to access protected areas.
.exceptionHandling()
.accessDeniedPage( "/403" )
.and()
// The intercept-url configuration is where we specify what roles are allowed access to what areas.
// We specifically force the connection to https for all the pages, although it could be sufficient
// just on the login page. The access parameter is where the expressions are used to control which
// roles can access specific areas. One of the most important things is the order of the intercept-urls,
// the most catch-all type patterns should at the bottom of the list as the matches are executed
// in the order they are configured below. So /** (anyRequest()) should always be at the bottom of the list.
.authorizeRequests()
.antMatchers( "/admin" ).hasRole("ADMIN")
.antMatchers("/login**").permitAll()
.antMatchers("/home").permitAll()
.antMatchers("/404").permitAll()
.anyRequest().authenticated()
.and()
// This is where we configure our login form.
// login-page: the page that contains the login screen
// login-processing-url: this is the URL to which the login form should be submitted
// default-target-url: the URL to which the user will be redirected if they login successfully
// authentication-failure-url: the URL to which the user will be redirected if they fail login
// username-parameter: the name of the request parameter which contains the username
// password-parameter: the name of the request parameter which contains the password
.formLogin()
.loginPage( "/login" )
.failureUrl( "/login?err=1" )
.defaultSuccessUrl("/private")
.usernameParameter( "username" )
.passwordParameter( "password" )
.permitAll()
.and()
// This is where the logout page and process is configured. The logout-url is the URL to send
// the user to in order to logout, the logout-success-url is where they are taken if the logout
// is successful, and the delete-cookies and invalidate-session make sure that we clean up after logout
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout=1")
.invalidateHttpSession(true)
//.deleteCookies("JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE")
.and()
.csrf()
.disable()
// The session management is used to ensure the user only has one session. This isn't
// compulsory but can add some extra security to your application.
.sessionManagement()
.maximumSessions(1);
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception
{
return super.userDetailsServiceBean();
}
}
SpringSecurityInitializer
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer{
//do nothing
}
With this config i'm not able to handle 404 error code.
Thanks in advance.
Updated to add more information about config files
Conclusion seems to be that setting throwExceptionIfNoHandlerFound to true does not throw an exception when no handler is found.
The solution is quite simple. From the javadoc # DispatcherServlet.setThrowExceptionIfNoHandlerFound. It states here that a NoHandlerFoundException will never be thrown if DefaultServletHttpRequestHandler is used.
Solution hence is to remove the line
configurer.enable();
from your MvcConfiguration. The exception should fire now and your GlobalExceptionHandler should do the rest!
Workaround: Add #RequestMapping("/**")
#Controller
#ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
#RequestMapping("/**")
public String handlerNotMappingRequest(HttpServletRequest request, HttpServletResponse response, HttpHeaders httpHeaders)
throws NoHandlerFoundException {
throw new NoHandlerFoundException("No handler mapping found.", request.getRequestURL().toString(), httpHeaders);
}
#ExceptionHandler(Throwable.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ModelAndView handleControllerException(Throwable ex) {
logger.error("ErrorLog: ", ex);
return new ModelAndView("error/exception", "exceptionMsg", "ExceptionHandler msg: " + ex.toString());
}
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex) {
logger.error("ErrorLog: ", ex);
return new ModelAndView("error/exception", "exceptionMsg", "NoHandlerFoundException msg: " + ex.toString());
}
}
The solution is to extend AbstractAnnotationConfigDispatcherServletInitializer and override this method:
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
final DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
OR this one:
#Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
And finally in your ControlerAdvice use this:
#ExceptionHandler(NoHandlerFoundException.class)
public String error404(Exception ex) {
return new ModelAndView("404");
}
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)
;
}
}