diff --git a/src/main/java/de/thm/arsnova/config/SecurityConfig.java b/src/main/java/de/thm/arsnova/config/SecurityConfig.java index cbb810fddca8544baf8279762f15c7dedbf54abd..63a018f2e15268cba4948975b941f76d53cfad6f 100644 --- a/src/main/java/de/thm/arsnova/config/SecurityConfig.java +++ b/src/main/java/de/thm/arsnova/config/SecurityConfig.java @@ -23,6 +23,8 @@ import de.thm.arsnova.security.LoginAuthenticationFailureHandler; import de.thm.arsnova.security.LoginAuthenticationSucessHandler; import de.thm.arsnova.security.CustomLdapUserDetailsMapper; import de.thm.arsnova.security.RegisteredUserDetailsService; +import de.thm.arsnova.security.jwt.JwtAuthenticationProvider; +import de.thm.arsnova.security.jwt.JwtTokenFilter; import de.thm.arsnova.security.pac4j.OauthCallbackFilter; import de.thm.arsnova.security.pac4j.OauthAuthenticationProvider; import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; @@ -53,6 +55,7 @@ import org.springframework.security.config.annotation.method.configuration.Enabl import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -67,6 +70,7 @@ import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; @@ -132,11 +136,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint()); http.csrf().disable(); http.headers() .addHeaderWriter(new HstsHeaderWriter(false)); + http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); if (casEnabled) { http.addFilter(casAuthenticationFilter()); http.addFilter(casLogoutFilter()); @@ -150,6 +156,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { List<String> providers = new ArrayList<>(); + auth.authenticationProvider(jwtAuthenticationProvider()); if (ldapEnabled) { providers.add("ldap"); auth.authenticationProvider(ldapAuthenticationProvider()); @@ -200,6 +207,17 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { return new Http403ForbiddenEntryPoint(); } + @Bean + public JwtAuthenticationProvider jwtAuthenticationProvider() { + return new JwtAuthenticationProvider(); + } + + @Bean + public JwtTokenFilter jwtTokenFilter() throws Exception { + JwtTokenFilter jwtTokenFilter = new JwtTokenFilter(); + return jwtTokenFilter; + } + @Bean LoginAuthenticationSucessHandler successHandler() { final LoginAuthenticationSucessHandler successHandler = new LoginAuthenticationSucessHandler(); diff --git a/src/main/java/de/thm/arsnova/security/jwt/JwtAuthenticationProvider.java b/src/main/java/de/thm/arsnova/security/jwt/JwtAuthenticationProvider.java index ac7d51b46379a822f8d249bd778603f459805918..569a4df5f46c8288a3290a80d7dde9726b21bc6a 100644 --- a/src/main/java/de/thm/arsnova/security/jwt/JwtAuthenticationProvider.java +++ b/src/main/java/de/thm/arsnova/security/jwt/JwtAuthenticationProvider.java @@ -1,6 +1,7 @@ package de.thm.arsnova.security.jwt; import de.thm.arsnova.security.User; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -8,10 +9,6 @@ import org.springframework.security.core.AuthenticationException; public class JwtAuthenticationProvider implements AuthenticationProvider { private JwtService jwtService; - public JwtAuthenticationProvider(final JwtService jwtService) { - this.jwtService = jwtService; - } - @Override public Authentication authenticate(final Authentication authentication) throws AuthenticationException { final String token = (String) authentication.getCredentials(); @@ -24,4 +21,9 @@ public class JwtAuthenticationProvider implements AuthenticationProvider { public boolean supports(final Class<?> aClass) { return JwtToken.class.isAssignableFrom(aClass); } + + @Autowired + public void setJwtService(final JwtService jwtService) { + this.jwtService = jwtService; + } } diff --git a/src/main/java/de/thm/arsnova/security/jwt/JwtService.java b/src/main/java/de/thm/arsnova/security/jwt/JwtService.java index f1620aaee8146fc16d6f9323a065b3681c6dc616..ee5bb47fffd51db137e57099bff9303f5e394207 100644 --- a/src/main/java/de/thm/arsnova/security/jwt/JwtService.java +++ b/src/main/java/de/thm/arsnova/security/jwt/JwtService.java @@ -71,6 +71,6 @@ public class JwtService { final Collection<GrantedAuthority> authorities = decodedJwt.getClaim(ROLES_CLAIM_NAME).asList(String.class).stream() .map(role -> new SimpleGrantedAuthority(ROLE_PREFIX + role)).collect(Collectors.toList()); - return new User(userService.get(userId), authorities); + return userService.loadUser(userId, authorities); } } diff --git a/src/main/java/de/thm/arsnova/security/jwt/JwtToken.java b/src/main/java/de/thm/arsnova/security/jwt/JwtToken.java index f7ce5cea3d421d8a3fed6e97572db5f8787ef87c..ecce14b5bebb2669ab1007b076a6cb858f2538dc 100644 --- a/src/main/java/de/thm/arsnova/security/jwt/JwtToken.java +++ b/src/main/java/de/thm/arsnova/security/jwt/JwtToken.java @@ -16,6 +16,7 @@ public class JwtToken extends AbstractAuthenticationToken { super(grantedAuthorities); this.token = token; this.principal = principal; + setAuthenticated(!grantedAuthorities.isEmpty()); } public JwtToken(final String token) { diff --git a/src/main/java/de/thm/arsnova/security/jwt/JwtTokenFilter.java b/src/main/java/de/thm/arsnova/security/jwt/JwtTokenFilter.java index 282a3c16b825e9fa3b2bc4fd9cd2c9e53ec010d4..f8c5e2e062e3676799da5e7154de2cd7722642da 100644 --- a/src/main/java/de/thm/arsnova/security/jwt/JwtTokenFilter.java +++ b/src/main/java/de/thm/arsnova/security/jwt/JwtTokenFilter.java @@ -1,29 +1,50 @@ package de.thm.arsnova.security.jwt; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.GenericFilterBean; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import java.io.IOException; -public class JwtTokenFilter extends AbstractAuthenticationProcessingFilter { +@Component +public class JwtTokenFilter extends GenericFilterBean { private static final String JWT_HEADER_NAME = "Arsnova-Auth-Token"; - - protected JwtTokenFilter() { - super(new AntPathRequestMatcher("/**")); - } + private static final Logger logger = LoggerFactory.getLogger(JwtTokenFilter.class); + private JwtAuthenticationProvider jwtAuthenticationProvider; @Override - public Authentication attemptAuthentication(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse) throws AuthenticationException { - String jwtHeader = httpServletRequest.getHeader(JWT_HEADER_NAME); - if (jwtHeader == null) { - throw new PreAuthenticatedCredentialsNotFoundException("No authentication header present."); + public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { + String jwtHeader = ((HttpServletRequest) servletRequest).getHeader(JWT_HEADER_NAME); + if (jwtHeader != null) { + JwtToken token = new JwtToken(jwtHeader); + try { + Authentication authenticatedToken = jwtAuthenticationProvider.authenticate(token); + if (authenticatedToken != null) { + logger.debug("Storing JWT to SecurityContext: {}", authenticatedToken); + SecurityContextHolder.getContext().setAuthentication(authenticatedToken); + } else { + logger.debug("Could not authenticate JWT."); + } + } catch (final Exception e) { + logger.debug("JWT authentication failed", e); + } + } else { + logger.debug("No authentication header present."); } - JwtToken token = new JwtToken(jwtHeader); + filterChain.doFilter(servletRequest, servletResponse); + } - return getAuthenticationManager().authenticate(token); + @Autowired + public void setJwtAuthenticationProvider(final JwtAuthenticationProvider jwtAuthenticationProvider) { + this.jwtAuthenticationProvider = jwtAuthenticationProvider; } } diff --git a/src/main/java/de/thm/arsnova/services/UserService.java b/src/main/java/de/thm/arsnova/services/UserService.java index 2fab0d042d3c909e4f37b868b47c9db678d1f0f4..814bcd84cfd9ab1356b23a3c93bf5bc201fa6f01 100644 --- a/src/main/java/de/thm/arsnova/services/UserService.java +++ b/src/main/java/de/thm/arsnova/services/UserService.java @@ -73,6 +73,8 @@ public interface UserService extends EntityService<UserProfile> { User loadUser(UserProfile.AuthProvider authProvider, String loginId, Collection<GrantedAuthority> grantedAuthorities, boolean autoCreate) throws UsernameNotFoundException; + User loadUser(String userId, Collection<GrantedAuthority> grantedAuthorities); + UserProfile getByAuthProviderAndLoginId(UserProfile.AuthProvider authProvider, String loginId); UserProfile getByUsername(String username); diff --git a/src/main/java/de/thm/arsnova/services/UserServiceImpl.java b/src/main/java/de/thm/arsnova/services/UserServiceImpl.java index c9ce4bd48c955d690f8c7387b66f0f6c8618af8d..87ef81be2697919b16e7cf2a89092622f4e91130 100644 --- a/src/main/java/de/thm/arsnova/services/UserServiceImpl.java +++ b/src/main/java/de/thm/arsnova/services/UserServiceImpl.java @@ -398,6 +398,18 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple return new User(userProfile, grantedAuthorities); } + @Override + public User loadUser(final String userId, final Collection<GrantedAuthority> grantedAuthorities) + throws UsernameNotFoundException { + logger.debug("Load user: UserId: {}", userId); + UserProfile userProfile = userRepository.findOne(userId); + if (userProfile == null) { + throw new UsernameNotFoundException("User does not exist."); + } + + return new User(userProfile, grantedAuthorities); + } + @Override public UserProfile getByAuthProviderAndLoginId(final UserProfile.AuthProvider authProvider, final String loginId) { return userRepository.findByAuthProviderAndLoginId(authProvider, loginId);