“You are the light of the world. A town built on a hill cannot be hidden.
[Matthew 5:14]

Github : securityexam

3. Basic Spring Security

    3. 1 About Spring Security



Spring Security is a powerful framework for handling security in Java-based applications. Primarily used in web applications, it provides security features such as user authentication and authorization.


The core features of Spring Security include:


1. Authentication: The process of verifying the identity of a user. Spring Security supports various authentication methods, such as basic form-based authentication with a username and password, and token-based authentication.


2. Authorization: Managing access rights for users. It verifies whether a user has the necessary permissions to access specific resources or functionality.


3. Security Configuration: Spring Security allows security-related settings to be configured using Java code or XML, enabling flexible configuration of security policies for applications.


4. Session Management: Managing user sessions and controlling session states, such as login and logout.


5. CSRF Protection: Providing features to defend against Cross-Site Request Forgery (CSRF) attacks.


6. Remember-Me Functionality: Supporting the 'Remember-Me' feature, which allows users to stay logged in.


Spring Security is a highly flexible and extensible framework capable of meeting various security requirements. It enables developers to implement robust security with simple configurations.




        Here's a detailed list of the topics


  • 3.1.1 Basic Spring Security Project:
  • 3.1.2 Spring Security Configuration:
  • 3.1.3 Basic Sign up , in , out::
  • 3.1.4 Authentication / Authorization





Table name -> never use "User"

(Member / Employee / Guest /Stuednt)

id -> username

pw->password

enabled, role




3.1.1 Basic Spring Security Project:


        [ File ] - [ New ] - [ Spring Start Project ]




   


3.1.2 Spring Security Configuration:


      application.properties


spring.application.name=securityexam

# Database Setting
spring.datasource.dbcp2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/securityexam
spring.datasource.username=root
spring.datasource.password=1111
# JPA
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.show_sql=true



      build.gradle

implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'




        create Database

 

   

      create Member.java Class

package com.example.securityexam;
import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;

@Data
@Entity
public class Member {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer mid;
@Column(unique = true)
private String username; // for SpringSecurity Policy
private String password; // for SpringSecurity Policy
private boolean enabled; // for SpringSecurity Policy
private String role; // for SpringSecurity Policy
@Column(unique = true)
private String memail;
private LocalDateTime mdate;

}


After writing Member.java, when you run the server, the table is automatically generated.


 

      create SecurityConfig.java Class


package com.example.securityexam;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests .requestMatchers(new AntPathRequestMatcher("/**")).permitAll()) ; return http.build(); } }



3.1.3 Basic Sign up , in , out:

       MemberRepository.java

package com.example.securityexam;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member, Integer> {

}


MemberService.java / MemberServiceImpl.java

package com.example.securityexam;

public interface MemberService {
      void create(Member member);
}



package com.example.securityexam;
import java.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class MemberServiceImpl implements MemberService {
@Autowired
private MemberRepository memberRepository;

@Override
public void create(Member member) {

member.setEnabled(true);
member.setRole("ROLE_USER"); //ROLE_ADMIN, ROLE_MANAGER, ROLE_PAID...
member.setMdate(LocalDateTime.now());

BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
member.setPassword(passwordEncoder.encode(member.getPassword()));
memberRepository.save(member);
}
}


        MemberController.java

package com.example.securityexam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class MemberController {

@Autowired
private MemberService memberService;
@GetMapping("/signup")
public String signup() {

return "signup";
}

@PostMapping("/signup")
public String signup(Member member) {

memberService.create(member);
return "signup";
}
}


      signup.html



<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>

<form action="/signup" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />

<p>ID : <input type="text" name="username">
<p>PW : <input type="password" name="password">
<p>Mail : <input type="text" name="memail">
<p><input type="submit" value="Sign up">

</form>

</body>
</html>





!! important !! you must include CSRF TOKEN

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />


    



Login check with Spring Security


      MemberRepository


      add --> Optional<Member> findByusername(String username); // login check

package com.example.securityexam;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member, Integer> {

Optional<Member> findByusername(String username); // login check

}


      MemberSecurityService.java ( for login check )



package com.example.securityexam;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
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.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;
@Service
public class MemberSecurityService implements UserDetailsService {
@Autowired
private MemberRepository memberRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

Optional<Member> tmember = memberRepository.findByusername(username);
if (tmember.isEmpty()) {
throw new UsernameNotFoundException("You need to Sign up first...");
}

Member member = tmember.get();

List<GrantedAuthority> authorities = new ArrayList<>();

if ("ROLE_USER".equals(member.getRole())) {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
} else if ("ROLE_MANAGER".equals(member.getRole())) {
authorities.add(new SimpleGrantedAuthority("ROLE_MANAGER"));
} else {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}

return new User(member.getUsername(), member.getPassword(), authorities);
}
}


     add beans to SecurityConfig.java


package com.example.securityexam;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests
.requestMatchers(new AntPathRequestMatcher("/**")).permitAll())
.formLogin((formLogin) -> formLogin
.loginPage("/login")
.defaultSuccessUrl("/")
)
.logout((logout) -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true))
;
return http.build();
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {

return authenticationConfiguration.getAuthenticationManager();
}
}


      signup.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<span sec:authorize="isAnonymous()">Login First...<a href="/login">log in</a></span>
<span sec:authorize="isAuthenticated()">Welcome...<span th:text="${#authentication.name}"></span>
<span th:text="${#authentication.authorities}"></span>
<a href="/logout">log out</a>
</span>


<form action="/signup" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<p>ID : <input type="text" name="username">
<p>PW : <input type="password" name="password">
<p>Mail : <input type="text" name="memail">
<p><input type="submit" value="Sign up">
</form>
</body>
</html>


      signin.html

<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form action="/login" method="post">
<div th:if="${param.error}">
<div class="alert alert-danger">
Please, Check your ID or Password...
</div>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<p>ID : <input type="text" name="username">
<p>PW : <input type="password" name="password">
<p><input type="submit" value="Sign in">
</form>
</body>
</html>



   

   



3.1.4 Authentication / Authorization


한국정보시스템개발원 |
Hankook Information System Institute

austiny@snu.ac.kr / austiny@gatech.edu