Add basic authentication

master
esensar 2017-05-29 09:27:53 +02:00
parent f14d1cbe55
commit 5c3a592a55
10 changed files with 273 additions and 47 deletions

View File

@ -38,6 +38,7 @@ dependencies {
compile('mysql:mysql-connector-java') compile('mysql:mysql-connector-java')
compile('org.hibernate:hibernate-validator') compile('org.hibernate:hibernate-validator')
compile('org.springframework.cloud:spring-cloud-starter-eureka') compile('org.springframework.cloud:spring-cloud-starter-eureka')
compile('io.jsonwebtoken:jjwt:0.7.0')
compile project(':common') compile project(':common')
testCompile('org.springframework.cloud:spring-cloud-starter-eureka-server') testCompile('org.springframework.cloud:spring-cloud-starter-eureka-server')

View File

@ -11,7 +11,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration @Configuration
public class UsersConfig { public class UsersConfig {
@Bean @Bean
public PasswordEncoder providePasswordEncoder(){ public static PasswordEncoder providePasswordEncoder(){
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
} }
} }

View File

@ -19,6 +19,13 @@ public class UserRole {
private String roleName; private String roleName;
public UserRole() {
}
public UserRole(String roleName) {
this.roleName = roleName;
}
public long getId() { public long getId() {
return id; return id;
} }
@ -34,4 +41,19 @@ public class UserRole {
public void setRoleName(String roleName) { public void setRoleName(String roleName) {
this.roleName = roleName; this.roleName = roleName;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserRole userRole = (UserRole) o;
return roleName != null ? roleName.equals(userRole.roleName) : userRole.roleName == null;
}
@Override
public int hashCode() {
return roleName != null ? roleName.hashCode() : 0;
}
} }

View File

@ -0,0 +1,36 @@
package ba.steleks.security;/**
* Created by ensar on 28/05/17.
*/
public class AccountCredentials {
private String username;
private String password;
private String role;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}

View File

@ -0,0 +1,26 @@
package ba.steleks.security;/**
* Created by ensar on 28/05/17.
*/
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
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 java.io.IOException;
public class JWTAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
Authentication authentication = TokenAuthenticationService.getAuthentication((HttpServletRequest) request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,42 @@
package ba.steleks.security;/**
* Created by ensar on 28/05/17.
*/
import ba.steleks.model.UserRole;
import com.fasterxml.jackson.databind.ObjectMapper;
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.web.authentication.AbstractAuthenticationProcessingFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
public JWTLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
super(defaultFilterProcessesUrl);
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
AccountCredentials creds = new ObjectMapper().readValue(request.getInputStream(), AccountCredentials.class);
return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(creds.getUsername(),
creds.getPassword(), Collections.emptyList()));
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
Set<UserRole> userRoles = UserRoleFactory.fromGrantedAuthorities(authResult.getAuthorities());
TokenAuthenticationService.addAuthenticationHeader(response, authResult.getName(), userRoles);
}
}

View File

@ -6,15 +6,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@ -22,23 +19,26 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class SecurityConfig extends WebSecurityConfigurerAdapter { public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean @Bean
public AuthenticationProvider provideAuthenticationProvider() { public UserDetailsService provideUserDetailsService() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); return new SteleksUsersDetailsService();
authenticationProvider.setUserDetailsService(new SteleksUsersDetailsService());
return authenticationProvider;
} }
@Autowired @Autowired
private AuthenticationProvider authProvider; private UserDetailsService userDetailsService;
@Override @Override
protected void configure( protected void configure(
AuthenticationManagerBuilder auth) throws Exception { AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider); auth.userDetailsService(userDetailsService);
} }
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated(); http.csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JWTLoginFilter("/accesstoken", authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
} }
} }

View File

@ -3,19 +3,22 @@ package ba.steleks.security;/**
*/ */
import ba.steleks.model.User; import ba.steleks.model.User;
import ba.steleks.model.UserRole;
import ba.steleks.repository.UsersJpaRepository; import ba.steleks.repository.UsersJpaRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors;
public class SteleksUsersDetailsService implements UserDetailsService { public class SteleksUsersDetailsService implements UserDetailsService {
private static final Logger logger =
Logger.getLogger(SteleksUsersDetailsService.class.getName());
@Autowired @Autowired
private UsersJpaRepository usersJpaRepository; private UsersJpaRepository usersJpaRepository;
@ -23,42 +26,17 @@ public class SteleksUsersDetailsService implements UserDetailsService {
@Override @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = usersJpaRepository.findByUsername(username); User user = usersJpaRepository.findByUsername(username);
UserDetails userDetails = new UserDetails() {
@Override if(user == null) {
public Collection<? extends GrantedAuthority> getAuthorities() { throw new UsernameNotFoundException(username);
return null;
} }
@Override List<GrantedAuthority> authorities =
public String getPassword() { UserRoleFactory.toGrantedAuthorities(user.getUserRoles());
return null;
}
@Override
public String getUsername() {
return null;
}
@Override return new org.springframework.security.core.userdetails.User(user.getUsername(),
public boolean isAccountNonExpired() { user.getPasswordHash(),
return false; authorities);
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
};
return userDetails;
} }
} }

View File

@ -0,0 +1,73 @@
package ba.steleks.security;
import ba.steleks.model.UserRole;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Date;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* Created by ensar on 28/05/17.
*/
class TokenAuthenticationService {
static final long EXPIRATION_TIME = 864_000_000; // 10 days
static final String SECRET = "ASteleksSecret";
static final String TOKEN_PREFIX = "Bearer";
static final String HEADER_STRING = "Authorization";
static final String ROLES = "roles";
static void addAuthenticationHeader(HttpServletResponse res, String username, Set<UserRole> userRoleSet) {
String JWT = Jwts.builder()
.setSubject(username)
.claim(ROLES, UserRoleFactory.toString(userRoleSet))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET).compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
}
static Authentication getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replaceFirst(TOKEN_PREFIX, ""))
.getBody();
String userName = claims.getSubject();
Set<UserRole> userRoles = UserRoleFactory.fromString(claims.get(ROLES, String.class));
if (userName != null) {
boolean access = false;
UserRole theUserRole = new UserRole("theUser");
if(userRoles.contains(theUserRole)) {
access = true;
}
if (access) {
return new UsernamePasswordAuthenticationToken(userName,
null,
UserRoleFactory.toGrantedAuthorities(userRoles));
}
else {
return new UsernamePasswordAuthenticationToken(userName,
null);
}
}
}
return null;
}
}

View File

@ -0,0 +1,48 @@
package ba.steleks.security;/**
* Created by ensar on 28/05/17.
*/
import ba.steleks.model.UserRole;
import com.google.common.collect.Lists;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.*;
import java.util.stream.Collectors;
public class UserRoleFactory {
public static String toString(Collection<UserRole> userRoleSet) {
// Transform to list
List<UserRole> userRoleList = Lists.newArrayList(userRoleSet);
// Sort by role name - to make different sets look the same in the end
userRoleList.sort(Comparator.comparing(UserRole::getRoleName));
// Transform to string
return userRoleList.stream().map(UserRole::getRoleName).collect(Collectors.joining(","));
}
public static Set<UserRole> fromString(String userRoleString) {
Set<UserRole> userRoles = new HashSet<>();
Arrays.stream(userRoleString.split(",")).map(UserRole::new).forEach(userRoles::add);
return userRoles;
}
public static List<GrantedAuthority> toGrantedAuthorities(Collection<UserRole> userRoleSet) {
return userRoleSet
.stream()
// get role name
.map(UserRole::getRoleName)
// create authority
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
public static Set<UserRole> fromGrantedAuthorities(Collection<? extends GrantedAuthority> grantedAuthorities) {
return grantedAuthorities
.stream()
.map(GrantedAuthority::getAuthority)
.map(UserRole::new)
.collect(Collectors.toSet());
}
}