diff --git a/build.gradle b/build.gradle index 756e24b..f8ada4f 100644 --- a/build.gradle +++ b/build.gradle @@ -52,11 +52,9 @@ repositories { dependencies { compile('org.springframework.boot:spring-boot-starter-actuator') - - //compile('org.springframework.boot:spring-boot-starter-security') - //compile("org.springframework.security.oauth:spring-security-oauth2:2.0.6.RELEASE") - //compile("org.springframework.cloud:spring-cloud-starter-security:1.0.0.RELEASE") - + compile('org.springframework.boot:spring-boot-starter-security') + compile('org.springframework.mobile:spring-mobile-device') + compile('io.jsonwebtoken:jjwt:0.6.0') compile('org.springframework.cloud:spring-cloud-starter-eureka') compile('org.springframework.boot:spring-boot-starter-amqp') compile('org.springframework.boot:spring-boot-starter-data-rest') diff --git a/src/main/java/company/tothepoint/BeheerApplication.java b/src/main/java/company/tothepoint/BeheerApplication.java index 9409a3c..4b2ca72 100644 --- a/src/main/java/company/tothepoint/BeheerApplication.java +++ b/src/main/java/company/tothepoint/BeheerApplication.java @@ -43,29 +43,6 @@ Queue queue() { return new Queue(BEHEER_QUEUE, true, false, false); } -// @EnableAuthorizationServer -// protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter { -// @Autowired -// private AuthenticationManager authenticationManager; -// -// @Override -// public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { -// endpoints.authenticationManager(authenticationManager); -// } -// -// @Override -// public void configure(ClientDetailsServiceConfigurer clients) -// throws Exception { -// clients.inMemory() -// .withClient("acme") -// .secret("acmesecret") -// .authorizedGrantTypes("authorization_code","implicit", -// "refresh_token", "password").scopes("openid"); -// -// } -// } - - @Bean Jackson2JsonMessageConverter jackson2JsonMessageConverter(ObjectMapper objectMapper) { @@ -79,15 +56,6 @@ TopicExchange beheerTopicExchange() { return new TopicExchange(BEHEER_EXCHANGE, true, false); } -// @Bean -// TopicExchange businessUnitTopicExchange() { -// return new TopicExchange(BUSINESSUNIT_EXCHANGE, true, false); -// } - -// @Bean -// Binding businessUnitBinding(Queue queue, TopicExchange businessUnitTopicExchange) { -// return BindingBuilder.bind(queue).to(businessUnitTopicExchange).with(BUSINESSUNIT_ROUTING); -// } @Bean Binding beheerBinding(Queue queue, TopicExchange beheerTopicExchange) { return BindingBuilder.bind(queue).to(beheerTopicExchange).with(BEHEER_ROUTING); diff --git a/src/main/java/company/tothepoint/configuration/CORSFilter.java b/src/main/java/company/tothepoint/configuration/CORSFilter.java new file mode 100644 index 0000000..8ce5473 --- /dev/null +++ b/src/main/java/company/tothepoint/configuration/CORSFilter.java @@ -0,0 +1,23 @@ +package company.tothepoint.configuration; + +import org.springframework.stereotype.Component; + +import javax.servlet.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class CORSFilter implements Filter { + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + HttpServletResponse response = (HttpServletResponse) res; + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE"); + response.setHeader("Access-Control-Max-Age", "3600"); + response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization"); + chain.doFilter(req, res); + } + + public voi init(FilterConfig filterConfig) {} + + public void destroy() {} +} diff --git a/src/main/java/company/tothepoint/configuration/WebSecurityConfiguration.java b/src/main/java/company/tothepoint/configuration/WebSecurityConfiguration.java new file mode 100644 index 0000000..694d403 --- /dev/null +++ b/src/main/java/company/tothepoint/configuration/WebSecurityConfiguration.java @@ -0,0 +1,92 @@ +package company.tothepoint.configuration; + +import company.tothepoint.security.AuthenticationTokenFilter; +import company.tothepoint.security.EntryPointUnauthorizedHandler; +import company.tothepoint.service.SecurityService; +import org.springframework.beans.factory.annotation.Autowired; +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.configuration.EnableWebSecurity; + +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +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.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + + @Autowired + private EntryPointUnauthorizedHandler unauthorizedHandler; + + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private SecurityService securityService; + + + @Autowired + public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + authenticationManagerBuilder + .userDetailsService(this.userDetailsService) + .passwordEncoder(passwordEncoder()); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Bean + public AuthenticationTokenFilter authenticationTokenFilterBean() throws Exception { + AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter(); + authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean()); + return authenticationTokenFilter; + } + + @Bean + public SecurityService securityService() { + return this.securityService; + } + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + httpSecurity + .csrf() + .disable() + .exceptionHandling() + .authenticationEntryPoint(this.unauthorizedHandler) + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() + .antMatchers("/auth/**").permitAll() + .anyRequest().authenticated(); + + // Custom JWT based authentication + httpSecurity + .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/src/main/java/company/tothepoint/controller/AuthenticationController.java b/src/main/java/company/tothepoint/controller/AuthenticationController.java new file mode 100644 index 0000000..2375a3e --- /dev/null +++ b/src/main/java/company/tothepoint/controller/AuthenticationController.java @@ -0,0 +1,74 @@ +package company.tothepoint.controller; +import org.springframework.mobile.device.Device; +import company.tothepoint.model.AuthenticationRequest; +import company.tothepoint.model.AuthenticationResponse; +import company.tothepoint.model.CerberusUser; +import company.tothepoint.security.TokenUtils; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@RestController +@RequestMapping("/auth") +public class AuthenticationController { + + private final Logger logger = Logger.getLogger(AuthenticationController.class); + + @Value("${beheerder-service.token.header}") + private String tokenHeader; + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private TokenUtils tokenUtils; + + @Autowired + private UserDetailsService userDetailsService; + + @RequestMapping(method = RequestMethod.POST) + public ResponseEntity authenticationRequest(@RequestBody AuthenticationRequest authenticationRequest, Device device) throws AuthenticationException { + logger.error("In POST REQUEST"); + logger.error(authenticationRequest.getUsername()); + logger.error(authenticationRequest.getPassword()); + Authentication authentication = this.authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken( + authenticationRequest.getUsername(), + authenticationRequest.getPassword() + ) + ); + + SecurityContextHolder.getContext().setAuthentication(authentication); + // Reload password post-authentication so we can generate token + UserDetails userDetails = this.userDetailsService.loadUserByUsername(authenticationRequest.getUsername()); + String token = this.tokenUtils.generateToken(userDetails, device); + return ResponseEntity.ok(new AuthenticationResponse(token)); + } + + @RequestMapping(value = "/refresh", method = RequestMethod.GET) + public ResponseEntity authenticationRequest(HttpServletRequest request) { + String token = request.getHeader(this.tokenHeader); + String username = this.tokenUtils.getUsernameFromToken(token); + CerberusUser user = (CerberusUser) this.userDetailsService.loadUserByUsername(username); + if (this.tokenUtils.canTokenBeRefreshed(token, user.getLastPasswordReset())) { + String refreshedToken = this.tokenUtils.refreshToken(token); + return ResponseEntity.ok(new AuthenticationResponse(refreshedToken)); + } else { + return ResponseEntity.badRequest().body(null); + } + } +} diff --git a/src/main/java/company/tothepoint/controller/BeheerderController.java b/src/main/java/company/tothepoint/controller/BeheerderController.java index 7a9c060..96733ca 100644 --- a/src/main/java/company/tothepoint/controller/BeheerderController.java +++ b/src/main/java/company/tothepoint/controller/BeheerderController.java @@ -12,8 +12,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -//import java.security.Principal; -import java.security.Principal; import java.util.List; import java.util.Optional; diff --git a/src/main/java/company/tothepoint/controller/ProtectedController.java b/src/main/java/company/tothepoint/controller/ProtectedController.java new file mode 100644 index 0000000..0ce5002 --- /dev/null +++ b/src/main/java/company/tothepoint/controller/ProtectedController.java @@ -0,0 +1,29 @@ +package company.tothepoint.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * Created by butrint on 9/05/16. + */ +@RestController +@RequestMapping("/protected") +public class ProtectedController { + + /** + This is an example of some different kinds of granular restriction for endpoints. You can use the built-in SPEL expressions + in @PreAuthorize such as 'hasRole()' to determine if a user has access. However, if you require logic beyond the methods + Spring provides then you can encapsulate it in a service and register it as a bean to use it within the annotation as + demonstrated below with 'securityService'. + **/ + @RequestMapping(method = RequestMethod.GET) + //@PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("@securityService.hasProtectedAccess()") + public ResponseEntity getDaHoney() { + return ResponseEntity.ok(":O"); + } + +} diff --git a/src/main/java/company/tothepoint/controller/UserController.java b/src/main/java/company/tothepoint/controller/UserController.java new file mode 100644 index 0000000..b836601 --- /dev/null +++ b/src/main/java/company/tothepoint/controller/UserController.java @@ -0,0 +1,41 @@ +package company.tothepoint.controller; + +import company.tothepoint.domain.User; +import company.tothepoint.repository.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.ShellProperties; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Created by butrint on 11/05/16. + */ +@RestController +@RequestMapping("/users") +public class UserController { + private static final Logger LOG = LoggerFactory.getLogger(UserController.class); + + @Autowired + private UserRepository userRepository; + @RequestMapping(method = RequestMethod.GET) + public ResponseEntity> getAllUsers() { + return new ResponseEntity<>(userRepository.findAll(), HttpStatus.OK); + } + + @RequestMapping(method = RequestMethod.POST) + public ResponseEntity createBeheerder(@RequestBody User user) { + LOG.debug("POST /users createUsers(..) called!"); + User createdUser = userRepository.save(user); + return new ResponseEntity<>(createdUser, HttpStatus.CREATED); + } + +} diff --git a/src/main/java/company/tothepoint/domain/DomainBase.java b/src/main/java/company/tothepoint/domain/DomainBase.java new file mode 100644 index 0000000..2db4e50 --- /dev/null +++ b/src/main/java/company/tothepoint/domain/DomainBase.java @@ -0,0 +1,15 @@ +package company.tothepoint.domain; + +import org.apache.commons.lang.builder.ReflectionToStringBuilder; + +import java.io.Serializable; + +/** + * Created by butrint on 10/05/16. + */ +public class DomainBase implements Serializable { + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } +} diff --git a/src/main/java/company/tothepoint/domain/User.java b/src/main/java/company/tothepoint/domain/User.java new file mode 100644 index 0000000..4982663 --- /dev/null +++ b/src/main/java/company/tothepoint/domain/User.java @@ -0,0 +1,84 @@ +package company.tothepoint.domain; +import com.fasterxml.jackson.annotation.JsonIgnore; +import company.tothepoint.domain.DomainBase; +import org.springframework.data.annotation.Id; + +import java.util.Date; + +public class User extends DomainBase { + + private static final long serialVersionUID = 2353528370345499815L; + private String id; + private String username; + @JsonIgnore + private String password; + private String email; + private Date lastPasswordReset; + private String authorities; + + public User() { + super(); + } + + public User(String username, String password, String email, Date lastPasswordReset, String authorities) { + this.setUsername(username); + this.setPassword(password); + this.setEmail(email); + this.setLastPasswordReset(lastPasswordReset); + this.setAuthorities(authorities); + } + + @Id + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + + public String getEmail() { + return this.email; + } + + public void setEmail(String email) { + this.email = email; + } + + + public Date getLastPasswordReset() { + return this.lastPasswordReset; + } + + public void setLastPasswordReset(Date lastPasswordReset) { + this.lastPasswordReset = lastPasswordReset; + } + + + public String getAuthorities() { + return this.authorities; + } + + public void setAuthorities(String authorities) { + this.authorities = authorities; + } + +} diff --git a/src/main/java/company/tothepoint/model/AuthenticationRequest.java b/src/main/java/company/tothepoint/model/AuthenticationRequest.java new file mode 100644 index 0000000..d112efd --- /dev/null +++ b/src/main/java/company/tothepoint/model/AuthenticationRequest.java @@ -0,0 +1,38 @@ +package company.tothepoint.model; + +/** + * Created by butrint on 4/05/16. + */ + +public class AuthenticationRequest extends ModelBase{ + + private static final long serialVersionUID = 6624726180748515507L; + private String username; + private String password; + + public AuthenticationRequest() { + super(); + } + + public AuthenticationRequest(String username, String password) { + this.setUsername(username); + this.setPassword(password); + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } +} + diff --git a/src/main/java/company/tothepoint/model/AuthenticationResponse.java b/src/main/java/company/tothepoint/model/AuthenticationResponse.java new file mode 100644 index 0000000..81d3e36 --- /dev/null +++ b/src/main/java/company/tothepoint/model/AuthenticationResponse.java @@ -0,0 +1,27 @@ +package company.tothepoint.model; + +/** + * Created by butrint on 4/05/16. + */ +public class AuthenticationResponse extends ModelBase { + + private static final long serialVersionUID = -6624726180748515507L; + private String token; + + public AuthenticationResponse() { + super(); + } + + public AuthenticationResponse(String token) { + this.setToken(token); + } + + public String getToken() { + return this.token; + } + + public void setToken(String token) { + this.token = token; + } + +} diff --git a/src/main/java/company/tothepoint/model/CerberusUser.java b/src/main/java/company/tothepoint/model/CerberusUser.java new file mode 100644 index 0000000..19e20ad --- /dev/null +++ b/src/main/java/company/tothepoint/model/CerberusUser.java @@ -0,0 +1,144 @@ +package company.tothepoint.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Date; + +public class CerberusUser implements UserDetails { + private String id; + private String username; + private String password; + private String email; + private Date lastPasswordReset; + private Collection authorities; + private Boolean accountNonExpired = true; + private Boolean accountNonLocked = true; + private Boolean credentialsNonExpired = true; + private Boolean enabled = true; + + + public CerberusUser() { + super(); + } + + public CerberusUser(String id, String username, String password, String email, Date lastPasswordReset, Collection authorities) { + this.setId(id); + this.setUsername(username); + this.setPassword(password); + this.setEmail(email); + this.setLastPasswordReset(lastPasswordReset); + this.setAuthorities(authorities); + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + @JsonIgnore + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return this.email; + } + + public void setEmail(String email) { + this.email = email; + } + + @JsonIgnore + public Date getLastPasswordReset() { + return this.lastPasswordReset; + } + + public void setLastPasswordReset(Date lastPasswordReset) { + this.lastPasswordReset = lastPasswordReset; + } + + @Override + public Collection getAuthorities() { + return this.authorities; + } + + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + + @JsonIgnore + public Boolean getAccountNonExpired() { + return this.accountNonExpired; + } + + public void setAccountNonExpired(Boolean accountNonExpired) { + this.accountNonExpired = accountNonExpired; + } + + @Override + public boolean isAccountNonExpired() { + return this.getAccountNonExpired(); + } + + @JsonIgnore + public Boolean getAccountNonLocked() { + return this.accountNonLocked; + } + + public void setAccountNonLocked(Boolean accountNonLocked) { + this.accountNonLocked = accountNonLocked; + } + + @Override + public boolean isAccountNonLocked() { + return this.getAccountNonLocked(); + } + + @JsonIgnore + public Boolean getCredentialsNonExpired() { + return this.credentialsNonExpired; + } + + public void setCredentialsNonExpired(Boolean credentialsNonExpired) { + this.credentialsNonExpired = credentialsNonExpired; + } + + @Override + public boolean isCredentialsNonExpired() { + return this.getCredentialsNonExpired(); + } + + @JsonIgnore + public Boolean getEnabled() { + return this.enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + @Override + public boolean isEnabled() { + return this.getEnabled(); + } + + +} diff --git a/src/main/java/company/tothepoint/model/CerberusUserFactory.java b/src/main/java/company/tothepoint/model/CerberusUserFactory.java new file mode 100644 index 0000000..6fa8de4 --- /dev/null +++ b/src/main/java/company/tothepoint/model/CerberusUserFactory.java @@ -0,0 +1,20 @@ +package company.tothepoint.model; + +import company.tothepoint.domain.User; +import org.springframework.security.core.authority.AuthorityUtils; + +/** + * Created by butrint on 10/05/16. + */ +public class CerberusUserFactory { + public static CerberusUser create(User user) { + return new CerberusUser( + user.getId(), + user.getUsername(), + user.getPassword(), + user.getEmail(), + user.getLastPasswordReset(), + AuthorityUtils.commaSeparatedStringToAuthorityList(user.getAuthorities()) + ); + } +} diff --git a/src/main/java/company/tothepoint/model/ModelBase.java b/src/main/java/company/tothepoint/model/ModelBase.java new file mode 100644 index 0000000..5eedb21 --- /dev/null +++ b/src/main/java/company/tothepoint/model/ModelBase.java @@ -0,0 +1,18 @@ +package company.tothepoint.model; + +/** + * Created by butrint on 10/05/16. + */ + +import org.apache.commons.lang.builder.ReflectionToStringBuilder; + +import java.io.Serializable; + +public class ModelBase implements Serializable { + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } + +} diff --git a/src/main/java/company/tothepoint/model/beheerder/beheerder.java b/src/main/java/company/tothepoint/model/beheerder/beheerder.java index fdbb7e0..6a86504 100644 --- a/src/main/java/company/tothepoint/model/beheerder/beheerder.java +++ b/src/main/java/company/tothepoint/model/beheerder/beheerder.java @@ -5,9 +5,6 @@ import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -/** - * Created by butrint on 22/04/16. - */ public class Beheerder { @Id private String id; @@ -24,9 +21,6 @@ public class Beheerder { @NotNull @Size(min = 1, max = 255) private String gebruikersNaam; - - - @NotNull @Size(min = 1, max = 255) private String passwoord; diff --git a/src/main/java/company/tothepoint/repository/UserRepository.java b/src/main/java/company/tothepoint/repository/UserRepository.java new file mode 100644 index 0000000..9ad15a0 --- /dev/null +++ b/src/main/java/company/tothepoint/repository/UserRepository.java @@ -0,0 +1,10 @@ +package company.tothepoint.repository; + +import company.tothepoint.domain.User; +import company.tothepoint.model.CerberusUser; +import org.springframework.data.mongodb.repository.MongoRepository; + +public interface UserRepository extends MongoRepository { + User findByUsername(String userName); +} + diff --git a/src/main/java/company/tothepoint/security/AuthenticationTokenFilter.java b/src/main/java/company/tothepoint/security/AuthenticationTokenFilter.java new file mode 100644 index 0000000..f6e1734 --- /dev/null +++ b/src/main/java/company/tothepoint/security/AuthenticationTokenFilter.java @@ -0,0 +1,54 @@ +package company.tothepoint.security; + +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter { + private final Logger logger = Logger.getLogger(AuthenticationTokenFilter.class); + + @Autowired + private TokenUtils tokenUtils; + + @Autowired + private UserDetailsService userDetailsService; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + logger.error("Do filter log"); + String authToken = httpRequest.getHeader("Authorization"); + logger.error("Authorization in dofilter log"); + logger.error(authToken); + String username = this.tokenUtils.getUsernameFromToken(authToken); + logger.error("Get username in dofilter log"); + logger.error(username); + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + logger.error("Inside first if dofilter log"); + UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); + if (this.tokenUtils.validateToken(authToken, userDetails)) { + logger.error("Inside second if dofilter log"); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + logger.error("Authentication in second if dofilter log"); + logger.error(authentication); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + + chain.doFilter(request, response); + } +} diff --git a/src/main/java/company/tothepoint/security/EntryPointUnauthorizedHandler.java b/src/main/java/company/tothepoint/security/EntryPointUnauthorizedHandler.java new file mode 100644 index 0000000..76a9c5b --- /dev/null +++ b/src/main/java/company/tothepoint/security/EntryPointUnauthorizedHandler.java @@ -0,0 +1,22 @@ +package company.tothepoint.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { + httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied"); + } +} + + diff --git a/src/main/java/company/tothepoint/security/TokenUtils.java b/src/main/java/company/tothepoint/security/TokenUtils.java new file mode 100644 index 0000000..33c08e8 --- /dev/null +++ b/src/main/java/company/tothepoint/security/TokenUtils.java @@ -0,0 +1,163 @@ +package company.tothepoint.security; + +import company.tothepoint.model.CerberusUser; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mobile.device.Device; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Component +public class TokenUtils { + private final Logger logger = Logger.getLogger(this.getClass()); + + @Value("${beheerder-service.token.secret}") + private String secret; + + @Value("${beheerder-service.token.expiration}") + private long expiration; + + private final String AUDIENCE_UNKNOWN = "unknown"; + private final String AUDIENCE_WEB = "web"; + private final String AUDIENCE_MOBILE = "mobile"; + private final String AUDIENCE_TABLET = "tablet"; + + public String getUsernameFromToken(String token) { + String username; + try { + final Claims claims = this.getClaimsFromToken(token); + username = claims.getSubject(); + } catch (Exception e) { + username = null; + } + return username; + } + + public Date getCreatedDateFromToken(String token) { + Date created; + try { + final Claims claims = this.getClaimsFromToken(token); + created = new Date((long) claims.get("created")); + } catch (Exception e) { + created = null; + } + return created; + } + + public Date getExpirationDateFromToken(String token) { + Date expiration; + try { + final Claims claims = this.getClaimsFromToken(token); + expiration = claims.getExpiration(); + } catch (Exception e) { + expiration = null; + } + return expiration; + } + + public String getAudienceFromToken(String token) { + String audience; + try { + final Claims claims = this.getClaimsFromToken(token); + audience = (String) claims.get("audience"); + } catch (Exception e) { + audience = null; + } + return audience; + } + + private Claims getClaimsFromToken(String token) { + Claims claims; + try { + claims = Jwts.parser() + .setSigningKey(this.secret) + .parseClaimsJws(token) + .getBody(); + } catch (Exception e) { + claims = null; + } + return claims; + } + + private Date generateCurrentDate() { + return new Date(System.currentTimeMillis()); + } + + private Date generateExpirationDate() { + return new Date(System.currentTimeMillis() + this.expiration * 1000); + } + + private Boolean isTokenExpired(String token) { + final Date expiration = this.getExpirationDateFromToken(token); + return expiration.before(this.generateCurrentDate()); + } + + private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) { + return (lastPasswordReset != null && created.before(lastPasswordReset)); + } + + private String generateAudience(Device device) { + String audience = this.AUDIENCE_UNKNOWN; + if (device.isNormal()) { + audience = this.AUDIENCE_WEB; + } else if (device.isTablet()) { + audience = AUDIENCE_TABLET; + } else if (device.isMobile()) { + audience = AUDIENCE_MOBILE; + } + return audience; + } + + private Boolean ignoreTokenExpiration(String token) { + String audience = this.getAudienceFromToken(token); + return (this.AUDIENCE_TABLET.equals(audience) || this.AUDIENCE_MOBILE.equals(audience)); + } + + public String generateToken(UserDetails userDetails, Device device) { + Map claims = new HashMap(); + claims.put("sub", userDetails.getUsername()); + claims.put("audience", this.generateAudience(device)); + claims.put("created", this.generateCurrentDate()); + return this.generateToken(claims); + } + + private String generateToken(Map claims) { + return Jwts.builder() + .setClaims(claims) + .setExpiration(this.generateExpirationDate()) + .signWith(SignatureAlgorithm.HS512, this.secret) + .compact(); + } + + public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) { + final Date created = this.getCreatedDateFromToken(token); + return (!(this.isCreatedBeforeLastPasswordReset(created, lastPasswordReset)) && (!(this.isTokenExpired(token)) || this.ignoreTokenExpiration(token))); + } + + public String refreshToken(String token) { + String refreshedToken; + try { + final Claims claims = this.getClaimsFromToken(token); + claims.put("created", this.generateCurrentDate()); + refreshedToken = this.generateToken(claims); + } catch (Exception e) { + refreshedToken = null; + } + return refreshedToken; + } + + public Boolean validateToken(String token, UserDetails userDetails) { + CerberusUser user = (CerberusUser) userDetails; + final String username = this.getUsernameFromToken(token); + final Date created = this.getCreatedDateFromToken(token); + final Date expiration = this.getExpirationDateFromToken(token); + return (username.equals(user.getUsername()) && !(this.isTokenExpired(token)) && !(this.isCreatedBeforeLastPasswordReset(created, user.getLastPasswordReset()))); + } +} diff --git a/src/main/java/company/tothepoint/service/SecurityService.java b/src/main/java/company/tothepoint/service/SecurityService.java new file mode 100644 index 0000000..3f4f2bd --- /dev/null +++ b/src/main/java/company/tothepoint/service/SecurityService.java @@ -0,0 +1,5 @@ +package company.tothepoint.service; + +public interface SecurityService { + public Boolean hasProtectedAccess(); +} diff --git a/src/main/java/company/tothepoint/service/impl/SecurityServiceImpl.java b/src/main/java/company/tothepoint/service/impl/SecurityServiceImpl.java new file mode 100644 index 0000000..474c448 --- /dev/null +++ b/src/main/java/company/tothepoint/service/impl/SecurityServiceImpl.java @@ -0,0 +1,14 @@ +package company.tothepoint.service.impl; + +import company.tothepoint.service.SecurityService; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Service +public class SecurityServiceImpl implements SecurityService{ + @Override + public Boolean hasProtectedAccess() { + return (SecurityContextHolder.getContext().getAuthentication().getAuthorities().contains(new SimpleGrantedAuthority("ADMIN"))); + } +} diff --git a/src/main/java/company/tothepoint/service/impl/UserDetailsServiceImpl.java b/src/main/java/company/tothepoint/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..88c7aa2 --- /dev/null +++ b/src/main/java/company/tothepoint/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,28 @@ +package company.tothepoint.service.impl; + +import company.tothepoint.domain.User; +import company.tothepoint.model.CerberusUser; +import company.tothepoint.model.CerberusUserFactory; +import company.tothepoint.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + @Autowired + private UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = this.userRepository.findByUsername(username); + + if (user == null) { + throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username)); + } else { + return CerberusUserFactory.create(user); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a7fb171..98cf971 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,6 +4,8 @@ spring: jackson: serialization: write_dates_as_timestamps: false + #INDENT_OUTPUT: true + eureka: instance: @@ -17,17 +19,24 @@ eureka: server: port: 8084 - contextPath: /uaa + +error: + whitelabel: + enabled=true -security: - user: - password: password +beheerder-service: + token: + header: Authorization + secret: flatMap43 + expiration: 604800 + logging: level: root: WARN company.tothepoint: DEBUG + --- spring: @@ -49,3 +58,4 @@ eureka: client: serviceUrl: defaultZone: http://discovery:8761/eureka/ +--- \ No newline at end of file