Skip to content

Feature/jwt implementation #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: 1-Bootstrap
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<description>Demo project for Spring Boot</description>

<properties>
<java.version>11</java.version>
<java.version>1.8</java.version>
</properties>

<dependencies>
Expand All @@ -29,6 +29,17 @@
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down
70 changes: 70 additions & 0 deletions src/main/java/com/example/demo/auth/ApplicationUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.example.demo.auth;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;
import java.util.Set;

public class ApplicationUser implements UserDetails {

private final Set<? extends GrantedAuthority> grantedAuthorities;
private final String password;
private final String username;
private final boolean isAccountNonExpired;
private final boolean isAccountNonLocked;
private final boolean isCredentialsNonExpired;
private final boolean isEnabled;

public ApplicationUser(String username,
String password,
Set<? extends GrantedAuthority> grantedAuthorities,
boolean isAccountNonExpired,
boolean isAccountNonLocked,
boolean isCredentialsNonExpired,
boolean isEnabled) {
this.grantedAuthorities = grantedAuthorities;
this.password = password;
this.username = username;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getUsername() {
return username;
}

@Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}

@Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}

@Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}

@Override
public boolean isEnabled() {
return isEnabled;
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/example/demo/auth/ApplicationUserDAO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.demo.auth;

import java.util.Optional;

public interface ApplicationUserDAO {

Optional<ApplicationUser> selectApplicationUserByUsername(String username);

}
25 changes: 25 additions & 0 deletions src/main/java/com/example/demo/auth/ApplicationUserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.demo.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;

@Service
public class ApplicationUserService implements UserDetailsService {

private final ApplicationUserDAO applicationUserDAO;

@Autowired
public ApplicationUserService(@Qualifier("fake") ApplicationUserDAO applicationUserDAO){
this.applicationUserDAO = applicationUserDAO;
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return applicationUserDAO.selectApplicationUserByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(String.format("Username %s not found", username)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.example.demo.auth;

import com.example.demo.security.ApplicationUserRole;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository("fake")
public class FakeApplicationUserDaoService implements ApplicationUserDAO {

private final PasswordEncoder passwordEncoder;

@Autowired
public FakeApplicationUserDaoService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}

@Override
public Optional<ApplicationUser> selectApplicationUserByUsername(String username) {
return getApplicationUsers()
.stream()
.filter(applicationUser -> username.equals(applicationUser.getUsername()))
.findFirst();
}

private List<ApplicationUser> getApplicationUsers() {
List<ApplicationUser> applicationUsers = Lists.newArrayList(
new ApplicationUser(
"annasmith",
passwordEncoder.encode("pass"),
ApplicationUserRole.STUDENT.getGrantedAuthorities(),
true,
true,
true,
true
),
new ApplicationUser(
"linda",
passwordEncoder.encode("pass"),
ApplicationUserRole.ADMIN.getGrantedAuthorities(),
true,
true,
true,
true
),
new ApplicationUser(
"tom",
passwordEncoder.encode("pass"),
ApplicationUserRole.ADMINTRAINEE.getGrantedAuthorities(),
true,
true,
true,
true
)
);
return applicationUsers;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/example/demo/contoller/TemplateController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.demo.contoller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/")
public class TemplateController {

@GetMapping("login")
public String getLogin() {
return "login";
}

@GetMapping("courses")
public String getCourses() {
return "courses";
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,93 @@
package com.example.demo.security;

import com.example.demo.auth.ApplicationUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.Cookie;
import java.net.CookieStore;
import java.util.concurrent.TimeUnit;

import static com.example.demo.security.ApplicationUserRole.*;


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {

private final PasswordEncoder passwordEncoder;
private final ApplicationUserService applicationUserService;

@Autowired
public ApplicationSecurityConfig(PasswordEncoder passwordEncoder, ApplicationUserService applicationUserService) {
this.applicationUserService = applicationUserService;
this.passwordEncoder = passwordEncoder;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "index", "/css/*", "/js/*")
.permitAll()
.antMatchers("/", "index", "/css/*", "/js/*").permitAll()
.antMatchers("/api/**").hasRole(STUDENT.name())
.anyRequest()
.authenticated()
.and()
.httpBasic();
.formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/courses", true)
.passwordParameter("password")
.usernameParameter("username")
.and()
.rememberMe()
.tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(21))
.key("somethingverysecured")
.rememberMeParameter("remember-me")
.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")) // https://docs.spring.io/spring-security/site/docs/4.2.12.RELEASE/apidocs/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me")
.logoutSuccessUrl("/login");
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}

@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsService(applicationUserService);
return provider;
}



public static void main(String[] args) {
System.out.println(TimeUnit.DAYS.toSeconds(1));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.demo.security;

public enum ApplicationUserPermission {
STUDENT_READ("student:read"),
STUDENT_WRITE("student:write"),
COURSE_READ("course:read"),
COURSE_WRITE("course:write");

private final String permission;

ApplicationUserPermission(String permission) {
this.permission = permission;
}

public String getPermission() {
return permission;
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/example/demo/security/ApplicationUserRole.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.example.demo.security;

import com.google.common.collect.Sets;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import java.util.Set;
import java.util.stream.Collectors;

import static com.example.demo.security.ApplicationUserPermission.*;

public enum ApplicationUserRole {
STUDENT(Sets.newHashSet()),
ADMIN(Sets.newHashSet(COURSE_READ, COURSE_WRITE, STUDENT_READ, STUDENT_WRITE)),
ADMINTRAINEE(Sets.newHashSet(COURSE_READ, STUDENT_READ));

private final Set<ApplicationUserPermission> permissions;

ApplicationUserRole(Set<ApplicationUserPermission> permissions) {
this.permissions = permissions;
}

public Set<ApplicationUserPermission> getPermissions() {
return permissions;
}

public Set<SimpleGrantedAuthority> getGrantedAuthorities() {
Set<SimpleGrantedAuthority> permissions = getPermissions().stream()
.map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
.collect(Collectors.toSet());
permissions.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
return permissions;
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/example/demo/security/PasswordConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.demo.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordConfig {

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/example/demo/student/Student.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,12 @@ public Integer getStudentId() {
public String getStudentName() {
return studentName;
}

@Override
public String toString() {
return "Student{" +
"studentId=" + studentId +
", studentName='" + studentName + '\'' +
'}';
}
}
Loading