Add basic authentication
parent
f14d1cbe55
commit
5c3a592a55
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
|
||||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if(user == null) {
|
||||||
public String getPassword() {
|
throw new UsernameNotFoundException(username);
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
List<GrantedAuthority> authorities =
|
||||||
public String getUsername() {
|
UserRoleFactory.toGrantedAuthorities(user.getUserRoles());
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAccountNonExpired() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return new org.springframework.security.core.userdetails.User(user.getUsername(),
|
||||||
public boolean isAccountNonLocked() {
|
user.getPasswordHash(),
|
||||||
return false;
|
authorities);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCredentialsNonExpired() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return userDetails;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue