diff --git a/src/main/java/de/rtuni/ms/as/Application.java b/src/main/java/de/rtuni/ms/as/Application.java index f024e1b8ffd0bdc448b68d7f6f46589643ea9b27..7ba464b0fbc1a64ef38a819b78df859159245a89 100644 --- a/src/main/java/de/rtuni/ms/as/Application.java +++ b/src/main/java/de/rtuni/ms/as/Application.java @@ -10,7 +10,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; /** - * Authentication service for microservice architecture. + * Class for starting authentication service. * * @author Julian * @@ -21,11 +21,11 @@ public class Application { //--------------------------------------------------------------------------------------------- /** - * Starts the application. + * Start the application. * * @param args The arguments */ - public static void main(final String[] args) { SpringApplication.run(Application.class, args); } + public static void main(String[] args) { SpringApplication.run(Application.class, args); } //--------------------------------------------------------------------------------------------- } diff --git a/src/main/java/de/rtuni/ms/as/JwtUsernameAndPasswordAuthenticationFilter.java b/src/main/java/de/rtuni/ms/as/JwtUsernameAndPasswordAuthenticationFilter.java deleted file mode 100644 index 3a8d7492875d90034f80cf9c29305348028c1f7c..0000000000000000000000000000000000000000 --- a/src/main/java/de/rtuni/ms/as/JwtUsernameAndPasswordAuthenticationFilter.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2019 (C) by Julian Horner. - * All Rights Reserved. - */ - -package de.rtuni.ms.as; - -import java.io.IOException; -import java.util.Collections; -import java.util.Date; -import java.util.stream.Collectors; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -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.GrantedAuthority; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; - -/** - * Filter class for json web token authentication of user name and password. - * - * @author Julian - */ -public class JwtUsernameAndPasswordAuthenticationFilter - extends UsernamePasswordAuthenticationFilter { - //---------------------------------------------------------------------------------------------- - - /** We use auth manager to validate the user credentials */ - private AuthenticationManager authManager; - - /** The configuration for the json web token. */ - private final JwtConfig jwtConfig; - - //---------------------------------------------------------------------------------------------- - - /** - * Creates an instance with the given <code>AuthenticationManager</code> and the given - * <code>JwtConfig</code>. - * - * @param authManager The stated manager - * @param jwtConfig The stated configuration for json web token - */ - public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authManager, - JwtConfig jwtConfig) { - this.authManager = authManager; - this.jwtConfig = jwtConfig; - - // By default, UsernamePasswordAuthenticationFilter listens to "/login" path. - // In our case, we use "/auth". So, we need to override the defaults. - this.setRequiresAuthenticationRequestMatcher( - new AntPathRequestMatcher(jwtConfig.getUri(), "POST")); - } - - //---------------------------------------------------------------------------------------------- - - /** - * Read the credentials from the given request and tries to authenticate them. - */ - @Override - public Authentication attemptAuthentication(HttpServletRequest requ, HttpServletResponse resp) - throws AuthenticationException { - try { - // Reads the credentials from the request body - // and put them in a newly created UserCredentials object. - UserCredentials credentials = - new ObjectMapper().readValue(requ.getInputStream(), UserCredentials.class); - - // Creates an authentication token object with the credentials from the request - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - credentials.getUsername(), credentials.getPassword(), Collections.emptyList()); - - // The manager tries to authenticate, it uses the loadUserByUsername() method in - // UserDetailsServiceImpl to load one of the embedded user. - return authManager.authenticate(authToken); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - //---------------------------------------------------------------------------------------------- - - /** - * Upon successful authentication, generate a token. The given <code>Authentication<code> object - * is the current authenticated user. - */ - @Override - protected void successfulAuthentication(HttpServletRequest requ, HttpServletResponse resp, - FilterChain chain, Authentication auth) throws IOException, ServletException { - Long now = System.currentTimeMillis(); - - // Building of the token - String token = Jwts.builder().setSubject(auth.getName()) - - // Convert authorities to list of strings - // This is important because it affects the way we get them back in the Gateway - .claim("authorities", - auth.getAuthorities().stream().map(GrantedAuthority::getAuthority) - .collect(Collectors.toList())) - .setIssuedAt(new Date(now)) - .setExpiration(new Date(now + jwtConfig.getExpiration() * 1000)) - - // Sign the token with a hash-based message authentication code,sha256 hash function - // and the given secret - .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret().getBytes()).compact(); - - // Add token to the header - resp.addHeader(jwtConfig.getHeader(), jwtConfig.getPrefix() + token); - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - } - - //---------------------------------------------------------------------------------------------- - - /** - * A (temporary) class to represent the user credentials. - * - * @author Julian - * - */ - @SuppressWarnings("unused") - private static class UserCredentials { - private String username; - private String password; - - public String getUsername() { return username; } - - public void setUsername(final String value) { username = value; } - - public String getPassword() { return password; } - - public void setPassword(final String value) { password = value; } - - } - - //---------------------------------------------------------------------------------------------- -} diff --git a/src/main/java/de/rtuni/ms/as/JwtConfig.java b/src/main/java/de/rtuni/ms/as/config/JWTConfiguration.java similarity index 55% rename from src/main/java/de/rtuni/ms/as/JwtConfig.java rename to src/main/java/de/rtuni/ms/as/config/JWTConfiguration.java index 9ecde7d16b2a2e883341489d9a2cc70573a35de0..ace92c88f227ac8b5bebd57311390a6efe61dd44 100644 --- a/src/main/java/de/rtuni/ms/as/JwtConfig.java +++ b/src/main/java/de/rtuni/ms/as/config/JWTConfiguration.java @@ -1,72 +1,77 @@ -/* - * Copyright 2019 (C) by Julian Horner. - * All Rights Reserved. - */ - -package de.rtuni.ms.as; - -import org.springframework.beans.factory.annotation.Value; - -/** - * Configuration class for json web token. - * - * @author Julian - * - */ -public class JwtConfig { - //---------------------------------------------------------------------------------------------- - - @Value("${security.jwt.uri:/auth/**}") - private String Uri; - - @Value("${security.jwt.header:Authorization}") - private String header; - - @Value("${security.jwt.prefix:Bearer}") - private String prefix; - - @Value("${security.jwt.expiration:#{24*60*60}}") - private int expiration; - - @Value("${security.jwt.secret:JwtSecretKey}") - private String secret; - - //---------------------------------------------------------------------------------------------- - - /** - * Get the uri. - * - * @return The uri - */ - public String getUri() { return Uri; } - - /** - * Get the header. - * - * @return The header - */ - public String getHeader() { return header; } - - /** - * Get the prefix. - * - * @return The prefix - */ - public String getPrefix() { return prefix; } - - /** - * Get the expiration. - * - * @return The expiration - */ - public int getExpiration() { return expiration; } - - /** - * Get the secret. - * - * @return The secret - */ - public String getSecret() { return secret; } - - //---------------------------------------------------------------------------------------------- -} +/* + * Copyright 2019 (C) by Julian Horner. + * All Rights Reserved. + */ + +package de.rtuni.ms.as.config; + +import org.springframework.beans.factory.annotation.Value; + +/** + * Configuration class for JWT. + * + * @author Julian + * + */ +public class JWTConfiguration { + //--------------------------------------------------------------------------------------------- + + /** Get the URI where the credentials needs to be send. */ + @Value("${security.jwt.uri:/auth/**}") + private String Uri; + + /** Get the header authorization type. */ + @Value("${security.jwt.header:Authorization}") + private String header; + + /** Get the prefix of the token message. */ + @Value("${security.jwt.prefix:Bearer}") + private String prefix; + + /** Get the expiration of the token in seconds. */ + @Value("${security.jwt.expiration:#{24*60*60}}") + private int expiration; + + /** Get the key for encryption and decryption. */ + @Value("${security.jwt.secret:JwtSecretKey}") + private String secret; + + //--------------------------------------------------------------------------------------------- + + /** + * Get the URI where the credentials needs to be send. + * + * @return The stated URI + */ + public String getUri() { return Uri; } + + /** + * Get the header authorization type. + * + * @return The stated header + */ + public String getHeader() { return header; } + + /** + * Get the prefix of the token message. + * + * @return The stated prefix + */ + public String getPrefix() { return prefix; } + + /** + * Get the expiration of the token in seconds. + * + * @return The stated expiration + */ + public int getExpiration() { return expiration; } + + /** + * Get the the key for encryption and decryption. + * + * @return The stated secret + */ + public String getSecret() { return secret; } + + //--------------------------------------------------------------------------------------------- +} diff --git a/src/main/java/de/rtuni/ms/as/SecurityConfiguration.java b/src/main/java/de/rtuni/ms/as/config/SecurityConfiguration.java similarity index 52% rename from src/main/java/de/rtuni/ms/as/SecurityConfiguration.java rename to src/main/java/de/rtuni/ms/as/config/SecurityConfiguration.java index 8172d794d247a225d124ec21b48689e3210bf900..34e0c4a802a70a9a865afcd201c6c0521944f8cb 100644 --- a/src/main/java/de/rtuni/ms/as/SecurityConfiguration.java +++ b/src/main/java/de/rtuni/ms/as/config/SecurityConfiguration.java @@ -1,109 +1,105 @@ -/* - * Copyright 2019 (C) by Julian Horner. - * All Rights Reserved. - */ - -package de.rtuni.ms.as; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -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; - -/** - * Class that enables custom security configuration. - * - * @author Julian - * - */ -@EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { - //---------------------------------------------------------------------------------------------- - - /** A service that loads users from the database. */ - @Autowired - private UserDetailsService userDetailsService; - - /** The <code>JwtConfig</code> for the json web token. */ - @Autowired - private JwtConfig jwtConfig; - - //---------------------------------------------------------------------------------------------- - - /** - * Overrides the default configuration. - */ - @Override - protected void configure(HttpSecurity http) throws Exception { - http.csrf().disable() - // make sure we use stateless session; session won't be used to store user's state. - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() - // handle an authorized attempts - .exceptionHandling().authenticationEntryPoint( - (req, rsp, e) -> { - rsp.setContentType("application/json"); - rsp.setCharacterEncoding("UTF-8"); - rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - ).and() - - /* - * Add a filter to validate user credentials and add token in the response header. - * - * What's the authenticationManager()? An object provided by WebSecurityConfigurerAdapter, - * used to authenticate the user passing user's credentials. The filter needs this - * authentication manager to authenticate the user. - */ - .addFilter(new JwtUsernameAndPasswordAuthenticationFilter( - authenticationManager(), jwtConfig)) - .authorizeRequests() - // allow all POST requests - .antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll() - // any other requests must be authenticated - .anyRequest().authenticated(); - } - - // TODO improve comment - /** - * Spring has <code>UserDetailsService</code> interface, which can be overridden to provide our - * implementation for fetching user from database (or any other source). - * <p> - * The <code>UserDetailsService</code> object is used by the authentication manager to load the - * user from database. In addition, we need to define the password encoder also. So, - * authentication manager can compare and verify passwords. - */ - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); - } - - //---------------------------------------------------------------------------------------------- - - /** - * Get a new <code>JwtConfig</code>. - * - * @return The stated configuration - */ - @Bean - public JwtConfig jwtConfig() { return new JwtConfig(); } - - //---------------------------------------------------------------------------------------------- - - /** - * Get a new <code>BCryptPasswordEncoder</code>. - * - * @return The stated encoder - */ - @Bean - public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } - - //---------------------------------------------------------------------------------------------- -} +/* + * Copyright 2019 (C) by Julian Horner. + * All Rights Reserved. + */ + +package de.rtuni.ms.as.config; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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 de.rtuni.ms.as.filter.JWTUsernameAndPasswordAuthenticationFilter; + +/** + * Class that handles several security configurations. + * + * @author Julian + * + */ +@EnableWebSecurity +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + //--------------------------------------------------------------------------------------------- + + /** The <code>JwtConfiguration</code>. */ + @Autowired + private JWTConfiguration jwtConfiguration; + + /** The service that loads users from the database. */ + @Autowired + private UserDetailsService userDetailsService; + + //--------------------------------------------------------------------------------------------- + + /** + * Get a new <code>JwtConfiguration</code>. + * + * @return The stated JWT configuration + */ + @Bean + public JWTConfiguration jwtConfiguration() { return new JWTConfiguration(); } + + //--------------------------------------------------------------------------------------------- + + /** + * Get a new <code>BCryptPasswordEncoder</code>. + * + * @return The stated encoder + */ + @Bean + public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } + + //--------------------------------------------------------------------------------------------- + + /** + * Configure custom security configurations. + */ + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable() + // Use stateless sessions. + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + + // Response if the user is unauthenticated. + .exceptionHandling().authenticationEntryPoint( + (req, rsp, e) -> { + rsp.setContentType("application/json"); + rsp.setCharacterEncoding("UTF-8"); + rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } + ).and() + + // Add a filter to validate user credentials with every request. + .addFilter(new JWTUsernameAndPasswordAuthenticationFilter( + authenticationManager(), jwtConfiguration)) + // The passed authentication manager is build in the configure() method below. + + .authorizeRequests() + // Permit all POST requests to auth path. + .antMatchers(HttpMethod.POST, jwtConfiguration.getUri()).permitAll() + // Any other request must be authenticated. + .anyRequest().authenticated(); + } + + /** + * The auth manager will use our implementation of the <code>UserDetailsService</code> + * interface to load the user. In addition, we define a password encoder so that the + * authentication manager is able to compare and verify passwords. + */ + @Override + protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception { + authManagerBuilder.userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); + } + + //--------------------------------------------------------------------------------------------- +} diff --git a/src/main/java/de/rtuni/ms/as/filter/JWTUsernameAndPasswordAuthenticationFilter.java b/src/main/java/de/rtuni/ms/as/filter/JWTUsernameAndPasswordAuthenticationFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..73d7296ee4a6da6242a6c0bebe9cf67e378cff0d --- /dev/null +++ b/src/main/java/de/rtuni/ms/as/filter/JWTUsernameAndPasswordAuthenticationFilter.java @@ -0,0 +1,148 @@ +/* + * Copyright 2019 (C) by Julian Horner. + * All Rights Reserved. + */ + +package de.rtuni.ms.as.filter; + +import java.io.IOException; +import java.util.Collections; +import java.util.Date; +import java.util.stream.Collectors; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +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.GrantedAuthority; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.rtuni.ms.as.config.JWTConfiguration; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * Filter class for authentication of user credentials, generating a JWT and adding the token to + * the response. + * + * @author Julian + */ +public class JWTUsernameAndPasswordAuthenticationFilter +extends UsernamePasswordAuthenticationFilter { + //--------------------------------------------------------------------------------------------- + + /** The <code>JwtConfiguration</code>. */ + private JWTConfiguration jwtConfiguration; + + /** The <code>AuthenticationManager</code> for validating user credentials. */ + private AuthenticationManager authManager; + + //--------------------------------------------------------------------------------------------- + + /** + * Creates an instance with the given <code>AuthenticationManager</code> and the given + * <code>JWTConfiguration</code>. + * + * @param authManager The stated manager + * @param config The stated configuration + */ + public JWTUsernameAndPasswordAuthenticationFilter(AuthenticationManager authManager, + JWTConfiguration config) { + this.authManager = authManager; + this.jwtConfiguration = config; + + // Overrides default path of UsernamePasswordAuthenticationFilter with auth path. + this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher( + config.getUri(), "POST")); + } + + //--------------------------------------------------------------------------------------------- + + /** + * Read the credentials from the given request, tries to authenticate them and returns the + * authenticated user token, or null if authentication is incomplete. + */ + @Override + public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) + throws AuthenticationException { + try { + // Reads the credentials from the request and put them in an UserCredentials object. + UserCredentials credentials = new ObjectMapper().readValue( + req.getInputStream(), UserCredentials.class); + // Creates a spring object for representing the credentials and inserts the previously + // created informations into it. + UsernamePasswordAuthenticationToken userPassAuth = + new UsernamePasswordAuthenticationToken( + credentials.getUsername(), + credentials.getPassword(), + Collections.emptyList() + ); + + // The manager tries to authenticate the credentials. + return authManager.authenticate(userPassAuth); + } catch (IOException e) { throw new RuntimeException(e); } + } + + //--------------------------------------------------------------------------------------------- + + /** + * Will be executed only upon successful authentication. Generate a token for the + * <code>Authentication</code> object that is returned from the + * <code>attemptAuthentication()</code> method. + */ + @Override + protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, + FilterChain chain, Authentication auth) throws IOException, ServletException { + Long now = System.currentTimeMillis(); + + // Builds the token + String token = Jwts.builder().setSubject(auth.getName()) + // Converts the authorities to Strings and append them to the builder. + .claim("authorities", auth.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toList())) + .setIssuedAt(new Date(now)) + .setExpiration(new Date(now + jwtConfiguration.getExpiration() * 1000)) + + // Signs the token with a hash-based message authentication code, a sha256 hash + // function and the given secret. + .signWith(SignatureAlgorithm.HS512, jwtConfiguration.getSecret().getBytes()) + // Builds the JWT. + .compact(); + + res.setStatus(HttpServletResponse.SC_NO_CONTENT); + // Add token to the Authorization header. + res.addHeader(jwtConfiguration.getHeader(), jwtConfiguration.getPrefix() + token); + } + + //--------------------------------------------------------------------------------------------- + + /** + * A (temporary) class for representing user credentials. + * + * @author Julian + * + */ + @SuppressWarnings("unused") + private static class UserCredentials { + private String username; + private String password; + + public String getUsername() { return username; } + public void setUsername(final String value) { username = value; } + + public String getPassword() { return password; } + public void setPassword(final String value) { password = value; } + + } + + //--------------------------------------------------------------------------------------------- +} diff --git a/src/main/java/de/rtuni/ms/as/UserDetailsServiceImpl.java b/src/main/java/de/rtuni/ms/as/service/UserDetailsServiceImpl.java similarity index 67% rename from src/main/java/de/rtuni/ms/as/UserDetailsServiceImpl.java rename to src/main/java/de/rtuni/ms/as/service/UserDetailsServiceImpl.java index 6cba832ede16c21153e426e7cdb6762633c2f790..df4583f33cea812e687fc6547da1dc70147722ba 100644 --- a/src/main/java/de/rtuni/ms/as/UserDetailsServiceImpl.java +++ b/src/main/java/de/rtuni/ms/as/service/UserDetailsServiceImpl.java @@ -1,115 +1,104 @@ -/* - * Copyright 2019 (C) by Julian Horner. - * All Rights Reserved. - */ - -package de.rtuni.ms.as; - -import java.util.Arrays; -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; - -/** - * Class that is able to load users. - * - * @author Julian - */ -@Service -public class UserDetailsServiceImpl implements UserDetailsService { - //---------------------------------------------------------------------------------------------- - - /** The password encoder. */ - @Autowired - private BCryptPasswordEncoder encoder; - - //---------------------------------------------------------------------------------------------- - - /** - * Loads a user by the given name. - * - * @param The stated name - */ - @Override - public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { - // hard coding users. All passwords must be encoded. - final List<AppUser> users = Arrays.asList( - new AppUser(1, "julian", encoder.encode("12345"), "USER"), - new AppUser(2, "admin", encoder.encode("12345"), "ADMIN")); - - for (AppUser appUser : users) { - if (appUser.getUsername().equals(username)) { - /* - * Remember that Spring needs roles to be in this format: "ROLE_" + userRole - * (i.e."ROLE_ADMIN") - * So, we need to set it to that format, so we can verify and compare roles - * (i.e. hasRole("ADMIN")). - */ - List<GrantedAuthority> grantedAuthorities = - AuthorityUtils.commaSeparatedStringToAuthorityList( - "ROLE_" + appUser.getRole()); - - /* - * The "User" class is provided by Spring and represents a model class for user to - * be returned by UserDetailsService and used by authentication manager to verify - * and check use authentication. - */ - return new User(appUser.getUsername(), appUser.getPassword(), grantedAuthorities); - } - } - - // If user not found. Throw this exception. - throw new UsernameNotFoundException("Username: " + username + " not found"); - } - - //---------------------------------------------------------------------------------------------- - - /** - * A (temporary) class represent the user saved in the database. - * - * @author Julian - * - */ - @SuppressWarnings("unused") - private static class AppUser { - private Integer id; - private String username; - private String password; - private String role; - - public AppUser(final Integer id, final String username, final String password, - final String role) { - this.id = id; - this.username = username; - this.password = password; - this.role = role; - } - - public Integer getId() { return id; } - - public void setId(final Integer value) { id = value; } - - public String getUsername() { return username; } - - public void setUsername(final String value) { username = value; } - - public String getPassword() { return password; } - - public void setPassword(final String value) { password = value; } - - public String getRole() { return role; } - - public void setRole(final String value) { role = value; } - - } - - //---------------------------------------------------------------------------------------------- -} +/* + * Copyright 2019 (C) by Julian Horner. + * All Rights Reserved. + */ + +package de.rtuni.ms.as.service; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +/** + * Class that is able to load users. + * + * @author Julian + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + //--------------------------------------------------------------------------------------------- + + /** The password encoder. */ + @Autowired + private BCryptPasswordEncoder encoder; + + //--------------------------------------------------------------------------------------------- + + /** + * Load a user by the given name. + * + * @param The stated name + */ + @Override + public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { + // Hard coding users, all passwords are encoded. + final List<AppUser> appUsers = Arrays.asList( + new AppUser(1, "default", encoder.encode("12345"), "USER"), + new AppUser(2, "admin", encoder.encode("12345"), "ADMIN")); + + String userRole; + for (AppUser appUser : appUsers) { + if (appUser.getUsername().equals(username)) { + // Spring needs roles to be in a format like "ROLE_" + user role. + userRole = "ROLE_" + appUser.getRole(); + + List<GrantedAuthority> grantedAuthorities = + AuthorityUtils.commaSeparatedStringToAuthorityList(userRole); + + // Since a specific Spring object has to be returned for the method, + // we convert our user into this object. + return new User(appUser.getUsername(), appUser.getPassword(), grantedAuthorities); + } + } + + // If the user is not found an exception is thrown. + throw new UsernameNotFoundException("Username: " + username + "wasn't found"); + } + + //--------------------------------------------------------------------------------------------- + + /** + * A (temporary) class for representing an user. + * + * @author Julian + * + */ + @SuppressWarnings("unused") + private static class AppUser { + private Integer id; + private String username; + private String password; + private String role; + + public AppUser(final Integer id, final String username, final String password, + final String role) { + this.id = id; + this.username = username; + this.password = password; + this.role = role; + } + + public Integer getId() { return id; } + public void setId(final Integer value) { id = value; } + + public String getUsername() { return username; } + public void setUsername(final String value) { username = value; } + + public String getPassword() { return password; } + public void setPassword(final String value) { password = value; } + + public String getRole() { return role; } + public void setRole(final String value) { role = value; } + } + + //--------------------------------------------------------------------------------------------- +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 98429eedf14fa981799900be0a20b25f97d8b996..5186bbfbb50f0153381ae08be0dc6888463f9b56 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,12 +1,10 @@ -# Spring properties spring: application: - name: auth-service # Identify this application + name: auth-service # Identifier of this application -# HTTP Server -server.port: 4444 # HTTP (Tomcat) port +server.port: 4444 eureka: client: serviceUrl: - defaultZone: http://localhost:8761/eureka/ \ No newline at end of file + defaultZone: http://localhost:8761/eureka/ # URL of the eureka server for registration \ No newline at end of file