Java tutorial
/* * MaritimeCloud Service Registry * Copyright (c) 2016 Frequentis AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.frequentis.maritime.mcsr.config; import com.frequentis.maritime.mcsr.repository.PersistentTokenRepository; import com.frequentis.maritime.mcsr.security.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents; import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter; import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter; import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import java.io.IOException; import javax.inject.Inject; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; @Configuration @EnableWebSecurity @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class) @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); @Inject private JHipsterProperties jHipsterProperties; @Inject private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler; @Inject private AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler; @Inject private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler; @Inject private Http401UnauthorizedEntryPoint authenticationEntryPoint; @Inject private UserDetailsService userDetailsService; @Inject private RememberMeServices rememberMeServices; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Inject public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(keycloakAuthenticationProvider()).userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); ; } @Override public void configure(WebSecurity web) throws Exception { log.debug("Configuring WebSecurity"); web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**").antMatchers("/app/**/*.{js,html}") .antMatchers("/bower_components/**").antMatchers("/i18n/**").antMatchers("/content/**") .antMatchers("/swagger-ui/index.html").antMatchers("/test/**").antMatchers("/h2-console/**"); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new NullAuthenticatedSessionStrategy(); } @Bean public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean( KeycloakAuthenticationProcessingFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); registrationBean.setEnabled(false); return registrationBean; } @Bean public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean( KeycloakPreAuthActionsFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); registrationBean.setEnabled(false); return registrationBean; } protected BasicAuthenticationFilter basicAuthenticationFilter() { try { return new BasicAuthenticationFilter(authenticationManager()); } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void configure(HttpSecurity http) throws Exception { //super.configure(http); log.debug("Configuring HttpSecurity"); log.debug("RememberMe service {}", rememberMeServices); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .sessionAuthenticationStrategy(sessionAuthenticationStrategy()).and() .addFilterBefore(basicAuthenticationFilter(), LogoutFilter.class) .addFilterBefore(new SkippingFilter(keycloakPreAuthActionsFilter()), LogoutFilter.class) .addFilterBefore(new SkippingFilter(keycloakAuthenticationProcessingFilter()), X509AuthenticationFilter.class) .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()).and() // .addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class) // .exceptionHandling() // .accessDeniedHandler(new CustomAccessDeniedHandler()) // .authenticationEntryPoint(authenticationEntryPoint) // .and() .rememberMe().rememberMeServices(rememberMeServices).rememberMeParameter("remember-me") .key(jHipsterProperties.getSecurity().getRememberMe().getKey()).and().formLogin() .loginProcessingUrl("/api/authentication").successHandler(ajaxAuthenticationSuccessHandler) .failureHandler(ajaxAuthenticationFailureHandler).usernameParameter("j_username") .passwordParameter("j_password").permitAll().and().logout().logoutUrl("/api/logout") .logoutSuccessHandler(ajaxLogoutSuccessHandler).deleteCookies("JSESSIONID", "CSRF-TOKEN") .permitAll().and().headers().frameOptions().disable().and().authorizeRequests() .antMatchers("/api/register").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/api/elasticsearch/**").permitAll().antMatchers("/api/activate").permitAll() .antMatchers("/api/authenticate").permitAll() .antMatchers("/api/account/reset_password/inactivateit").permitAll() .antMatchers("/api/account/reset_password/finish").permitAll().antMatchers("/api/profile-info") .permitAll().antMatchers("/websocket/tracker").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/websocket/**").permitAll().antMatchers("/management/**") .hasAuthority(AuthoritiesConstants.ADMIN).antMatchers("/v2/api-docs/**").permitAll() .antMatchers(HttpMethod.PUT, "/api/**").authenticated().antMatchers(HttpMethod.POST, "/api/**") .authenticated().antMatchers(HttpMethod.DELETE, "/api/**").authenticated() .antMatchers(HttpMethod.TRACE, "/api/**").authenticated().antMatchers(HttpMethod.HEAD, "/api/**") .authenticated().antMatchers(HttpMethod.PATCH, "/api/**").authenticated() .antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll().antMatchers(HttpMethod.GET, "/api/**") .permitAll().antMatchers("/swagger-resources/configuration/ui").permitAll() .antMatchers("/swagger-ui/index.html").permitAll().and().csrf().disable(); } @Bean public SecurityEvaluationContextExtension securityEvaluationContextExtension() { return new SecurityEvaluationContextExtension(); } /** * SkippingFilter skips holded filter if user is authenticated. * * <p>It used as workaround for Keycloak filter.</p> * */ class SkippingFilter implements Filter { private final Logger log = LoggerFactory.getLogger(SkippingFilter.class); private Filter f; public SkippingFilter(Filter filter) { f = filter; } @Override public void init(FilterConfig filterConfig) throws ServletException { f.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String url = ""; String queryString = ""; if (request instanceof HttpServletRequest) { url = ((HttpServletRequest) request).getRequestURL().toString(); queryString = ((HttpServletRequest) request).getQueryString(); } log.debug("Check authentication [" + url + "?" + queryString + "]"); if (isAuthenticated()) { log.debug("Authenticated - skip filter [" + url + "?" + queryString + "]"); chain.doFilter(request, response); } else { log.debug("No authentication - do filter [" + url + "?" + queryString + "]"); f.doFilter(request, response, chain); } } @Override public void destroy() { f.destroy(); } private boolean isAuthenticated() { return SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated(); } } }