Skip to content
Snippets Groups Projects
Commit 6f8abd83 authored by Thomas's avatar Thomas
Browse files

Added basic JWT auth

parent e94572fd
No related branches found
No related tags found
No related merge requests found
Showing
with 463 additions and 64 deletions
......@@ -31,6 +31,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
......
......@@ -3,13 +3,20 @@ package mozen;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@SpringBootApplication
@EnableJpaRepositories(basePackageClasses = MozenApplication.class)
@EntityScan(basePackageClasses = MozenApplication.class)
public class MozenApplication {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
SpringApplication.run(MozenApplication.class, args);
}
......
package mozen.auth;
import java.io.IOException;
import java.security.Key;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import mozen.model.LoginMessage;
import mozen.utils.KeyGenerator;
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
private AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
try {
LoginMessage message = new ObjectMapper().readValue(req.getInputStream(), LoginMessage.class);
System.err.println("Auth user n:"+message.getUsername()+" p:"+message.getPassword());
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(message.getUsername(), message.getPassword(), new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth)
throws IOException, ServletException {
User user = (User) auth.getPrincipal();
Key key = KeyGenerator.generateKey();
String token = Jwts.builder()
.setSubject(user.getUsername())
.claim("username",user.getUsername())
.setIssuedAt(new Date())
.setExpiration(toDate(LocalDateTime.now().plusDays(1L)))
.signWith(SignatureAlgorithm.HS512, key)
.compact();
res.addHeader("Authorization", "Bearer " + token);
}
private Date toDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
}
\ No newline at end of file
package mozen.auth;
import java.io.IOException;
import java.security.Key;
import java.util.ArrayList;
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.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import io.jsonwebtoken.Jwts;
import mozen.utils.KeyGenerator;
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = request.getHeader("Authorization");
if(header == null || !header.startsWith("Bearer ")) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken auth = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(auth);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if(token != null) {
Key key = KeyGenerator.generateKey();
String username = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token.replace("Bearer ", ""))
.getBody()
.getSubject();
if (username != null) {
return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
}
return null;
}
return null;
}
}
\ No newline at end of file
package mozen.auth;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Service;
import mozen.business.IUserManager;
@Service
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
IUserManager manager;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
mozen.model.User user = manager.getUserByUsername(username);
if(user == null) throw new UsernameNotFoundException(username);
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
}
\ No newline at end of file
package mozen.auth;
import org.springframework.beans.factory.annotation.Autowired;
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.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter{
@Autowired
private JwtUserDetailsService userDetailsService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/signup").permitAll()
.antMatchers(HttpMethod.GET, "/search").permitAll()
.antMatchers(HttpMethod.GET, "/models").permitAll()
.antMatchers(HttpMethod.GET, "/models/tags").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
\ No newline at end of file
......@@ -3,14 +3,14 @@ package mozen.business;
import java.util.Collection;
import mozen.model.Model;
import mozen.model.Tag;
import mozen.model.TagCategory;
public interface IModelDao {
void addModel(Model m);
void updateModel(Model m);
void removeModel(long id);
Collection<Tag> getTags();
Collection<TagCategory> getTags();
Model findModel(long id);
Collection<Model> findModelsByName(String name);
......
......@@ -3,7 +3,7 @@ package mozen.business;
import java.util.Collection;
import mozen.model.Model;
import mozen.model.Tag;
import mozen.model.TagCategory;
public interface IModelManager {
Long addModel(Model m);
......@@ -11,6 +11,6 @@ public interface IModelManager {
void removeModel(Long id);
Model getModel(Long id);
Collection<Tag> getTags();
Collection<TagCategory> getTags();
Collection<Model> findModel(String name);
}
\ No newline at end of file
......@@ -8,5 +8,5 @@ public interface IUserDao {
void removeUser(long id);
User findUser(long id);
User findUserByMail(String email);
User findUserByUsername(String username);
}
\ No newline at end of file
package mozen.business;
import mozen.model.SignupMessage;
import mozen.model.User;
public interface IUserManager {
Long addUser(User u);
Long addUser(SignupMessage message);
void updateUser(User u);
void removeUser(Long id);
User getUser(Long id);
User getUserByUsername(String username);
String logUser(String email, String password);
boolean resetPassword(String email);
boolean changePassword(String token, String password);
}
\ No newline at end of file
......@@ -11,7 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import mozen.model.Model;
import mozen.model.Tag;
import mozen.model.TagCategory;
@Repository
@Transactional
......@@ -55,9 +55,9 @@ public class ModelDao implements IModelDao {
}
@Override
public Collection<Tag> getTags() {
String query = "SELECT t FROM Tag t";
TypedQuery<Tag> q = em.createQuery(query, Tag.class);
public Collection<TagCategory> getTags() {
String query = "SELECT t FROM TagCategory t";
TypedQuery<TagCategory> q = em.createQuery(query, TagCategory.class);
try {
return q.getResultList();
} catch (Exception e) {
......
......@@ -6,7 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import mozen.model.Model;
import mozen.model.Tag;
import mozen.model.TagCategory;
@Service
public class ModelManager implements IModelManager {
......@@ -43,7 +43,7 @@ public class ModelManager implements IModelManager {
}
@Override
public Collection<Tag> getTags() {
public Collection<TagCategory> getTags() {
return dao.getTags();
}
......
......@@ -40,10 +40,10 @@ public class UserDao implements IUserDao {
}
@Override
public User findUserByMail(String email) {
String query = "SELECT u FROM User u WHERE u.email = : email";
public User findUserByUsername(String username) {
String query = "SELECT u FROM User u WHERE u.username = : username";
TypedQuery<User> q = em.createQuery(query, User.class);
q.setParameter("email", email);
q.setParameter("username", username);
try {
return q.getSingleResult();
} catch (Exception e) {
......
package mozen.business;
import java.security.Key;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import mozen.model.SignupMessage;
import mozen.model.User;
import mozen.utils.KeyGenerator;
@Service
public class UserManager implements IUserManager {
......@@ -19,8 +13,17 @@ public class UserManager implements IUserManager {
@Autowired
IUserDao dao;
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public Long addUser(User u) {
public Long addUser(SignupMessage message) {
User u = new User();
u.setEmail(message.getEmail());
u.setUsername(message.getUsername());
u.setPassword(bCryptPasswordEncoder.encode(message.getPassword()));
u.setRole("DEFAULT");
dao.addUser(u);
return u.getId();
}
......@@ -40,28 +43,6 @@ public class UserManager implements IUserManager {
return dao.findUser(id);
}
@Override
public String logUser(String email, String password) {
User u = dao.findUserByMail(email);
System.err.println("[AUTH] user e:"+email+" p:"+password+" u:"+u);
if(u == null) return null;
if(!u.getPassword().equals(password)) return null;
Key key = KeyGenerator.generateKey();
String token = Jwts.builder()
.setSubject(u.getId().toString())
.claim("id",u.getId())
.setIssuedAt(new Date())
.setExpiration(toDate(LocalDateTime.now().plusDays(1L)))
.signWith(SignatureAlgorithm.HS512, key)
.compact();
return token;
}
@Override
public boolean resetPassword(String email) {
// TODO Auto-generated method stub
......@@ -74,8 +55,9 @@ public class UserManager implements IUserManager {
return false;
}
private Date toDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
@Override
public User getUserByUsername(String username) {
return dao.findUserByUsername(username);
}
}
\ No newline at end of file
......@@ -5,7 +5,7 @@ import java.io.Serializable;
public class LoginMessage implements Serializable {
private static final long serialVersionUID = 1L;
private String email;
private String username;
private String password;
......@@ -13,17 +13,17 @@ public class LoginMessage implements Serializable {
public LoginMessage() {
}
public LoginMessage(String email, String password) {
this.email = email;
public LoginMessage(String username, String password) {
this.username = username;
this.password = password;
}
public String getEmail() {
return this.email;
public String getUsername() {
return this.username;
}
public void setEmail(String email) {
this.email = email;
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
......
......@@ -21,6 +21,8 @@ import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.format.annotation.DateTimeFormat;
@Entity
@Table
public class Model implements Serializable{
......@@ -46,6 +48,16 @@ public class Model implements Serializable{
@Size(min = 0, max = 5000)
private String longDescription;
@Basic
@DateTimeFormat(pattern = "yyyy-mm-dd")
@Column
private String added;
@Basic
@DateTimeFormat(pattern = "yyyy-mm-dd")
@Column
private String lastModified;
@Basic
@Column
private int votes;
......@@ -71,11 +83,13 @@ public class Model implements Serializable{
public Model() {
}
public Model(Long id, String name, String shortDescription, String longDescription, int votes, String filePath, User author, Set<Tag> tags, Set<CustomLayer> customLayers) {
public Model(Long id, String name, String shortDescription, String longDescription, String added, String lastModified, int votes, String filePath, User author, Set<Tag> tags, Set<CustomLayer> customLayers) {
this.id = id;
this.name = name;
this.shortDescription = shortDescription;
this.longDescription = longDescription;
this.added = added;
this.lastModified = lastModified;
this.votes = votes;
this.filePath = filePath;
this.author = author;
......@@ -83,7 +97,6 @@ public class Model implements Serializable{
this.customLayers = customLayers;
}
public Long getId() {
return this.id;
}
......@@ -116,6 +129,22 @@ public class Model implements Serializable{
this.longDescription = longDescription;
}
public String getAdded() {
return this.added;
}
public void setAdded(String added) {
this.added = added;
}
public String getLastModified() {
return this.lastModified;
}
public void setLastModified(String lastModified) {
this.lastModified = lastModified;
}
public int getVotes() {
return this.votes;
}
......
package mozen.model;
import java.io.Serializable;
import java.util.Collection;
public class SearchResult implements Serializable {
private static final long serialVersionUID = 1L;
private int total;
private int page;
private Collection<Model> models;
public SearchResult() {
}
public SearchResult(int total, int page, Collection<Model> models) {
this.total = total;
this.page = page;
this.models = models;
}
public int getTotal() {
return this.total;
}
public void setTotal(int total) {
this.total = total;
}
public int getPage() {
return this.page;
}
public void setPage(int page) {
this.page = page;
}
public Collection<Model> getModels() {
return this.models;
}
public void setModels(Collection<Model> models) {
this.models = models;
}
}
\ No newline at end of file
package mozen.model;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class SignupMessage implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Size(min = 1, max = 30)
private String username;
@NotNull
@Size(min = 1, max = 60)
//@Pattern(regexp="(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*-_=+])[a-zA-Z0-9!@#$%^&*-_=+](?=\\S+$).{8,35}")
private String password;
@NotNull
@Size(min = 1, max = 30)
@Pattern(regexp="([a-z0-9])+([.]([a-z0-9])+)?@([a-z])+(([.]([a-z])+)?|([-]([a-z])+)?)+.([a-z]){2,}")
private String email;
public SignupMessage() {
}
public SignupMessage(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
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;
}
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
......@@ -26,8 +27,8 @@ public class Tag implements Serializable{
private Long id;
@Basic
@Column(nullable = false)
private String category;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private TagCategory category;
@Basic
@Column(nullable = false)
......@@ -40,7 +41,7 @@ public class Tag implements Serializable{
public Tag() {
}
public Tag(Long id, String category, String name, Set<Model> models) {
public Tag(Long id, TagCategory category, String name, Set<Model> models) {
this.id = id;
this.category = category;
this.name = name;
......@@ -55,11 +56,12 @@ public class Tag implements Serializable{
this.id = id;
}
public String getCategory() {
@JsonIgnore
public TagCategory getCategory() {
return this.category;
}
public void setCategory(String category) {
public void setCategory(TagCategory category) {
this.category = category;
}
......
package mozen.model;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table
public class TagCategory implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Basic
@Column(nullable = false)
private String name;
@Basic
@OneToMany(mappedBy = "category", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Tag> tags;
public TagCategory() {
}
public TagCategory(Long id, String name, Set<Tag> tags) {
this.id = id;
this.name = name;
this.tags = tags;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Set<Tag> getTags() {
return this.tags;
}
public void setTags(Set<Tag> tags) {
this.tags = tags;
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment