“You are the light of the world. A town built on a hill cannot be hidden.
[Matthew 5:14]
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