- Added entities repos
- Added JWT Authorization - Added JWT Authentication - Added AccountService - Added UserRESTController
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
package me.aski.catalogueserviceauth;
|
||||
|
||||
import me.aski.catalogueserviceauth.dao.RoleRepository;
|
||||
import me.aski.catalogueserviceauth.dao.UserRepository;
|
||||
import me.aski.catalogueserviceauth.service.AccountService;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CatalogueServiceAuthApplication {
|
||||
@@ -10,5 +18,29 @@ public class CatalogueServiceAuthApplication {
|
||||
SpringApplication.run(CatalogueServiceAuthApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandLineRunner start(AccountService accountService, UserRepository userRepository, RoleRepository roleRepository) {
|
||||
return args -> {
|
||||
|
||||
accountService.clearDB();
|
||||
//userRepository.save(new User(null,"Abdellahaski","111111",true,null));
|
||||
accountService.saveRole("ADMIN");
|
||||
accountService.saveRole("USER");
|
||||
Stream.of("AbdellahASKI", "user1", "user2", "user3").forEach(username -> {
|
||||
accountService.saveUser(username, "1234", "1234");
|
||||
});
|
||||
accountService.addRoleToUser("AbdellahASKI", "ADMIN");
|
||||
|
||||
System.out.println(accountService.loadUserByUsername("AbdellahASKI"));
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
BCryptPasswordEncoder getBCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package me.aski.catalogueserviceauth.dao;
|
||||
|
||||
import me.aski.catalogueserviceauth.entities.Role;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
|
||||
@RepositoryRestResource
|
||||
public interface RoleRepository extends MongoRepository<Role, String> {
|
||||
public Role findByRoleName(String roleName);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package me.aski.catalogueserviceauth.dao;
|
||||
|
||||
import me.aski.catalogueserviceauth.entities.User;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
|
||||
@RepositoryRestResource
|
||||
public interface UserRepository extends MongoRepository<User, String> {
|
||||
|
||||
public User findByUsername(String username);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.aski.catalogueserviceauth.entities;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Document
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Role {
|
||||
@Id
|
||||
private String id;
|
||||
private String roleName;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Role{" +
|
||||
"id='" + id + '\'' +
|
||||
", roleName='" + roleName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.aski.catalogueserviceauth.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.springframework.data.mongodb.core.index.Indexed;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@Document
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class User {
|
||||
private String id;
|
||||
@Indexed(unique = true)
|
||||
private String username;
|
||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
private String password;
|
||||
private boolean active;
|
||||
@DBRef
|
||||
private Collection<Role> roles = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package me.aski.catalogueserviceauth.security;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import me.aski.catalogueserviceauth.entities.User;
|
||||
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.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
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.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class JWTAuthentificationFilter extends UsernamePasswordAuthenticationFilter {
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
public JWTAuthentificationFilter(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||
try {
|
||||
User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
|
||||
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Invalid Request: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
|
||||
Authentication authResult) throws IOException, ServletException {
|
||||
org.springframework.security.core.userdetails.User user =
|
||||
(org.springframework.security.core.userdetails.User) authResult.getPrincipal();
|
||||
List<String> roles = new ArrayList<>();
|
||||
authResult.getAuthorities().forEach(role -> {
|
||||
roles.add(role.getAuthority());
|
||||
});
|
||||
String jwt = JWT.create()
|
||||
.withIssuer(request.getRequestURI())
|
||||
.withSubject(user.getUsername())
|
||||
.withArrayClaim("roles", roles.toArray(new String[0]))
|
||||
.withExpiresAt(new Date(System.currentTimeMillis() + SecurityParams.EXPIRATION))
|
||||
.sign(Algorithm.HMAC256(SecurityParams.SECRET));
|
||||
System.out.println(jwt);
|
||||
response.addHeader(SecurityParams.HEADER_NAME, SecurityParams.PREFIX + jwt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package me.aski.catalogueserviceauth.security;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.JWTVerifier;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
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.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class JWTAuthorizationFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
|
||||
String allHeaders = "No-Auth, Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With";
|
||||
response.setHeader("Access-Control-Allow-Headers", allHeaders);
|
||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
|
||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
} else if (request.getRequestURI().equalsIgnoreCase("/login")) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
} else {
|
||||
|
||||
String jwtToken = request.getHeader(SecurityParams.HEADER_NAME);
|
||||
if (jwtToken == null || !jwtToken.startsWith(SecurityParams.PREFIX)) {
|
||||
//throw new RuntimeException("Not Authorized");
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
String jwt = jwtToken.substring(SecurityParams.PREFIX.length());
|
||||
|
||||
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SecurityParams.SECRET))
|
||||
.build();
|
||||
DecodedJWT decodedJWT = verifier.verify(jwt);
|
||||
String username = decodedJWT.getSubject();
|
||||
List<String> roles = decodedJWT.getClaims().get("roles").asList(String.class);
|
||||
|
||||
Collection<GrantedAuthority> authorities = roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
|
||||
|
||||
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(username, null, authorities);
|
||||
SecurityContextHolder.getContext().setAuthentication(user);
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package me.aski.catalogueserviceauth.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final BCryptPasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
public SecurityConfig(@Qualifier("userDetailsServiceImpl") UserDetailsService userDetailsService, BCryptPasswordEncoder passwordEncoder) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests().antMatchers("/login/**", "/register/**").permitAll()
|
||||
.and()
|
||||
.authorizeRequests().antMatchers("/users/**", "/roles/**").hasAuthority("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.addFilter(new JWTAuthentificationFilter(authenticationManager()))
|
||||
.addFilterBefore(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package me.aski.catalogueserviceauth.security;
|
||||
|
||||
public interface SecurityParams {
|
||||
public final static String HEADER_NAME = "Authorization";
|
||||
public final static String SECRET = "abdellah@aski.me";
|
||||
public final static long EXPIRATION = 10 * 24 * 3600;
|
||||
public final static String PREFIX = "Bearer ";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.aski.catalogueserviceauth.security;
|
||||
|
||||
import me.aski.catalogueserviceauth.entities.User;
|
||||
import me.aski.catalogueserviceauth.service.AccountService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
private final AccountService accountService;
|
||||
|
||||
@Autowired
|
||||
public UserDetailsServiceImpl(AccountService accountService) {
|
||||
this.accountService = accountService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
User user = accountService.loadUserByUsername(username);
|
||||
if (user == null) throw new UsernameNotFoundException("Invalid User");
|
||||
Collection<GrantedAuthority> authorities = user.getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getRoleName())).collect(Collectors.toList());
|
||||
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.aski.catalogueserviceauth.service;
|
||||
|
||||
import me.aski.catalogueserviceauth.entities.Role;
|
||||
import me.aski.catalogueserviceauth.entities.User;
|
||||
|
||||
public interface AccountService {
|
||||
User saveUser(String username, String password, String rePassword);
|
||||
|
||||
Role saveRole(String roleName);
|
||||
|
||||
User loadUserByUsername(String username);
|
||||
|
||||
User addRoleToUser(String username, String role);
|
||||
|
||||
void clearDB();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package me.aski.catalogueserviceauth.service;
|
||||
|
||||
import me.aski.catalogueserviceauth.dao.RoleRepository;
|
||||
import me.aski.catalogueserviceauth.dao.UserRepository;
|
||||
import me.aski.catalogueserviceauth.entities.Role;
|
||||
import me.aski.catalogueserviceauth.entities.User;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class AccountServiceImpl implements AccountService {
|
||||
|
||||
|
||||
private final RoleRepository roleRepository;
|
||||
private final UserRepository userRepository;
|
||||
private BCryptPasswordEncoder passwordEncoder;
|
||||
|
||||
//@Autowired
|
||||
public AccountServiceImpl(RoleRepository roleRepository, UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
|
||||
this.roleRepository = roleRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User saveUser(String username, String password, String rePassword) {
|
||||
username = username.toLowerCase();
|
||||
if (userRepository.findByUsername(username) != null) throw new RuntimeException("Username already exist");
|
||||
if (rePassword == null || rePassword.isEmpty() || !password.equals(rePassword))
|
||||
throw new RuntimeException("Please confirm your password");
|
||||
|
||||
User user = new User();
|
||||
user.setUsername(username);
|
||||
user.setPassword(passwordEncoder.encode(password));
|
||||
user.setActive(true);
|
||||
user.setRoles(new ArrayList<>());
|
||||
userRepository.save(user);
|
||||
|
||||
return addRoleToUser(username, "USER");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Role saveRole(String roleName) {
|
||||
return roleRepository.save(new Role(null, roleName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public User loadUserByUsername(String username) {
|
||||
return userRepository.findByUsername(username.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public User addRoleToUser(String username, String role) {
|
||||
User user = loadUserByUsername(username);
|
||||
user.getRoles().add(roleRepository.findByRoleName(role));
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearDB() {
|
||||
userRepository.deleteAll();
|
||||
roleRepository.deleteAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.aski.catalogueserviceauth.web;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import me.aski.catalogueserviceauth.entities.User;
|
||||
import me.aski.catalogueserviceauth.service.AccountService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class UserController {
|
||||
|
||||
private final AccountService accountService;
|
||||
|
||||
@Autowired
|
||||
public UserController(AccountService accountService) {
|
||||
this.accountService = accountService;
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public User register(@RequestBody UserForm userForm) {
|
||||
System.out.println(userForm.toString());
|
||||
return accountService.saveUser(userForm.getUsername(), userForm.getPassword(), userForm.getRePassword());
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
class UserForm {
|
||||
private String username;
|
||||
private String password;
|
||||
private String rePassword;
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.data.mongodb.uri=mongodb://localhost:27017/CatalogueService
|
||||
Reference in New Issue
Block a user