Design Patterns in Java

This commit is contained in:
Javadevjournal
2021-12-06 21:39:37 -08:00
parent 1b940d44bb
commit d95980639f
20 changed files with 526 additions and 16 deletions

80
design-patterns/pom.xml Normal file
View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javadevjournal</groupId>
<artifactId>design-patterns</artifactId>
<version>1.0-SNAPSHOT</version>
<name>design-patterns</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.1.0</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -0,0 +1,5 @@
package com.javadevjournal.designpatterns.factory.account;
public interface BankAccount {
void registerAccount();
}

View File

@@ -0,0 +1,8 @@
package com.javadevjournal.designpatterns.factory.account;
public class BusinessAccount implements BankAccount {
@Override
public void registerAccount() {
System.out.println("Creating a business account");
}
}

View File

@@ -0,0 +1,8 @@
package com.javadevjournal.designpatterns.factory.account;
public class CheckingAccount implements BankAccount{
@Override
public void registerAccount() {
System.out.println("Creating a checking account");
}
}

View File

@@ -0,0 +1,8 @@
package com.javadevjournal.designpatterns.factory.account;
public class PersonalAccount implements BankAccount{
@Override
public void registerAccount() {
System.out.println("Creating a personal account");
}
}

View File

@@ -0,0 +1,48 @@
package com.javadevjournal.designpatterns.factory;
import com.javadevjournal.designpatterns.factory.account.BankAccount;
import com.javadevjournal.designpatterns.factory.account.BusinessAccount;
import com.javadevjournal.designpatterns.factory.account.CheckingAccount;
import com.javadevjournal.designpatterns.factory.account.PersonalAccount;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
public class FactoryDesignPatternTest {
private BankAccountFactory factory;
@Before
public void setUp() {
factory = new BankAccountFactory();
}
@Test
public void testPersonalAccount(){
BankAccount account = factory.createAccount("P");
assertThat(account, instanceOf(PersonalAccount.class));
}
@Test
public void testBusinessAccount(){
BankAccount account = factory.createAccount("B");
assertThat(account, instanceOf(BusinessAccount.class));
}
@Test
public void testCheckingAccount(){
BankAccount account = factory.createAccount("C");
assertThat(account, instanceOf(CheckingAccount.class));
}
@Test
public void testValidAccountType(){
BankAccount account = factory.createAccount("X");
assertNull(account);
}
}

View File

@@ -0,0 +1,9 @@
package com.javadevjournal.datatypes;
public class DataType {
public static void main(String[] args) {
byte maxByteValue = 127;
System.out.println(maxByteValue);
}
}

View File

@@ -104,6 +104,38 @@
</exclusion>
</exclusions>
</dependency>
<!-- LDAP dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>1.5.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.7</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
@@ -111,7 +143,15 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>10</source>
<target>10</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,16 @@
package com.javadevjournal.config;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
/**
* EnableGlobalMethodSecurity to allow method level Spring security annotation for our application
*/
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class MethodSecurityConfig {
//default configuration class
}

View File

@@ -1,16 +1,23 @@
package com.javadevjournal.core.security;
import com.javadevjournal.core.security.authentication.CustomAuthenticationProvider;
import com.javadevjournal.core.security.filter.CustomAuthenticationFilter;
import com.javadevjournal.core.security.filter.CustomHeaderAuthFilter;
import com.javadevjournal.core.security.handlers.CustomAccessDeniedHandler;
import com.javadevjournal.core.security.handlers.CustomSuccessHandler;
import com.javadevjournal.core.security.handlers.LoginAuthenticationFailureHandler;
import com.javadevjournal.core.security.web.authentication.CustomWebAuthenticationDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -20,12 +27,18 @@ import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.LdapAuthenticator;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
@EnableWebSecurity
@@ -40,6 +53,9 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Resource
CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
@@ -63,7 +79,8 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
//Login configurations
.and()
.formLogin().defaultSuccessUrl("/account/home")
.formLogin()
.defaultSuccessUrl("/account/home")
.loginPage("/login")
.failureUrl("/login?error=true")
.successHandler(successHandler())
@@ -80,14 +97,29 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
http.authorizeRequests().antMatchers("/admim/**").hasAuthority("ADMIN");
http.addFilterAfter(customHeaderAuthFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public CustomHeaderAuthFilter customHeaderAuthFilter(){
return new CustomHeaderAuthFilter();
}
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
return new AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails>() {
@Override
public CustomWebAuthenticationDetails buildDetails(
HttpServletRequest request) {
return new CustomWebAuthenticationDetails(request);
}
};
}
/**
* <p></p>Creating bean for the custom suucess handler. You can use the custom success handlers for
* different use cases like</p>
@@ -106,6 +138,15 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
return new CustomAccessDeniedHandler();
}
@Bean
public CustomAuthenticationFilter authFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(failureHandler());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
@Bean
public LoginAuthenticationFailureHandler failureHandler(){
LoginAuthenticationFailureHandler failureHandler = new LoginAuthenticationFailureHandler();
@@ -165,7 +206,9 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
*/
@Override
protected void configure(AuthenticationManagerBuilder auth){
auth.authenticationProvider(authProvider());
//auth.authenticationProvider(authProvider());
auth.authenticationProvider(customAuthenticationProvider)
.authenticationProvider(authProvider());
}
/**
@@ -180,4 +223,19 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
db.setDataSource(dataSource);
return db;
}
//Spring security LDAP configurations
@Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
return authenticator;
}
@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
}

View File

@@ -0,0 +1,50 @@
package com.javadevjournal.core.security.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
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.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Resource
UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
if (StringUtils.isEmpty(username)) {
throw new BadCredentialsException("invalid login details");
}
// get user details using Spring security user details service
UserDetails user = null;
try {
user = userDetailsService.loadUserByUsername(username);
} catch (UsernameNotFoundException exception) {
throw new BadCredentialsException("invalid login details");
}
return createSuccessfulAuthentication(authentication, user);
}
private Authentication createSuccessfulAuthentication(final Authentication authentication, final UserDetails user) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), authentication.getCredentials(), user.getAuthorities());
token.setDetails(authentication.getDetails());
return token;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}

View File

@@ -3,6 +3,7 @@ package com.javadevjournal.core.security.core.userdetail;
import com.javadevjournal.core.user.jpa.data.Group;
import com.javadevjournal.core.user.jpa.data.UserEntity;
import com.javadevjournal.core.user.jpa.repository.UserRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -12,22 +13,32 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
@Service("userDetailsService")
@Transactional
public class CustomUserDetailService implements UserDetailsService{
@Autowired
@Resource
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
final UserEntity customer = userRepository.findByEmail(email);
//Let's split the email to get both values
String[] usernameAndCustomToken = StringUtils.split(email, String.valueOf(Character.LINE_SEPARATOR));
//if the String arrays is empty or size is not equal to 2, let's throw exception
if(Objects.isNull(usernameAndCustomToken) || usernameAndCustomToken.length !=2){
throw new UsernameNotFoundException("User not found");
}
final String userName = usernameAndCustomToken[0];
final String customToken = usernameAndCustomToken[1]; // use it based on your requirement
final UserEntity customer = userRepository.findByEmail(userName);
if (customer == null) {
throw new UsernameNotFoundException(email);
}
@@ -41,6 +52,8 @@ public class CustomUserDetailService implements UserDetailsService{
return user;
}
private Collection<GrantedAuthority> getAuthorities(UserEntity user){
Set<Group> userGroups = user.getUserGroups();
Collection<GrantedAuthority> authorities = new ArrayList<>(userGroups.size());

View File

@@ -0,0 +1,41 @@
package com.javadevjournal.core.security.filter;
import org.springframework.lang.Nullable;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final String customToken = "jdjCustomToken";
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
UsernamePasswordAuthenticationToken authRequest =getAuth(request);
return super.attemptAuthentication(request, response);
}
private UsernamePasswordAuthenticationToken getAuth(final HttpServletRequest request){
String username = obtainUsername(request);
String password = obtainPassword(request);
String customToken = obtainCustomToken(request);
String usernameDomain = String.format("%s%s%s", username.trim(),
String.valueOf(Character.LINE_SEPARATOR), customToken);
return new UsernamePasswordAuthenticationToken(
usernameDomain, password);
}
@Nullable
protected String obtainCustomToken(HttpServletRequest request) {
return request.getParameter(customToken);
}
}

View File

@@ -0,0 +1,32 @@
package com.javadevjournal.core.security.filter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomHeaderAuthFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
var request = (HttpServletRequest) servletRequest;
var response = (HttpServletResponse)servletResponse;
//if header is missing , send un-athorized error back
String authHeader = request.getHeader("X-HEADER");
if(StringUtils.isEmpty(authHeader)){
response.setStatus(
HttpServletResponse.SC_UNAUTHORIZED);
}
else{
filterChain.doFilter(servletRequest, servletResponse);
}
}
}

View File

@@ -0,0 +1,17 @@
package com.javadevjournal.core.security.token;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
public class CustomUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
private String customToken;
public CustomUsernamePasswordAuthenticationToken(Object principal, Object credentials, String customToken) {
super(principal, credentials);
this.customToken = customToken;
}
}

View File

@@ -0,0 +1,43 @@
package com.javadevjournal.core.security.web.authentication;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
private final String token;
/**
* Records the remote address and will also set the session Id if a session already
* exists (it won't create one).
*
* @param request that the authentication request was received from
*/
public CustomWebAuthenticationDetails(HttpServletRequest request) {
super(request);
this.token = request.getParameter("jdjCustomToken");
}
@Override
public String toString() {
return "CustomWebAuthenticationDetails{" +
"token='" + token + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
CustomWebAuthenticationDetails that = (CustomWebAuthenticationDetails) o;
return Objects.equals(token, that.token);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), token);
}
}

View File

@@ -18,6 +18,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import java.util.Objects;
@@ -25,25 +26,25 @@ import java.util.Objects;
@Service("customerAccountService")
public class DefaultCustomerAccountService implements CustomerAccountService {
@Autowired
@Resource
UserService userService;
@Autowired
@Resource
private SecureTokenService secureTokenService;
@Autowired
@Resource
SecureTokenRepository secureTokenRepository;
@Value("${site.base.url.https}")
private String baseURL;
@Autowired
@Resource
private EmailService emailService;
@Autowired
@Resource
private UserRepository userRepository;
@Autowired
@Resource
private PasswordEncoder passwordEncoder;

View File

@@ -9,6 +9,7 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@@ -17,7 +18,7 @@ import java.util.List;
@RequestMapping("/account")
public class AccountController {
@Autowired
@Resource
private SessionRegistry sessionRegistry;
@GetMapping("/home")

View File

@@ -0,0 +1,25 @@
package com.javadevjournal.web.controller.api;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v2/users")
public class UserRestAPI {
@GetMapping("/hello")
public String getData(){
return "hello";
}
@Secured({"ROLE_CUSTOMER", "ROLE_USER"})
@PreAuthorize("hasRole('ROLE_CUSTOMER') or hasRole('ROLE_ADMIN')")
@GetMapping("/user/{id}")
public String getUserById(@PathVariable String id){
return "id";
}
}

View File

@@ -62,7 +62,14 @@
</div>
</div>
</div>
<div class="input-group mb-3">
<input type="text" name="jdjCustomToken" class="form-control" placeholder="Token">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-8">
<div class="icheck-primary">