Java Design patterns

This commit is contained in:
Javadevjournal
2022-03-20 09:29:17 -07:00
parent 2577dfe0f0
commit aa5cb0a341
74 changed files with 366 additions and 1502 deletions

View File

@@ -1,8 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public abstract class AbstractFactory {
abstract Shape getShape(String shapeType) ;
}

View File

@@ -1,29 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class AbstractFactoryDemo {
public static void main(String[] args) {
/* get shape factory */
AbstractFactory shapeFactory = FactoryOfFactory.getFactory(false);
/* get an object of Shape Rectangle */
Shape shape1 = shapeFactory.getShape("RECTANGLE");
/* call draw method of Shape Rectangle */
shape1.drawShape();
/* get an object of Shape Square */
Shape shape2 = shapeFactory.getShape("SQUARE");
/* call draw method of Shape Square */
shape2.drawShape();
/* get rounded shape factory */
AbstractFactory roundShapeFactory = FactoryOfFactory.getFactory(true);
/* get an object of Shape Rectangle */
Shape shape3 = roundShapeFactory.getShape("RECTANGLE");
/* call draw method of Shape Rectangle */
shape3.drawShape();
/* get an object of Shape Square */
Shape shape4 = roundShapeFactory.getShape("SQUARE");
/* call draw method of Shape Square */
shape4.drawShape();
}
}

View File

@@ -1,14 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class FactoryOfFactory {
public static AbstractFactory getFactory(boolean rounded){
if(rounded){
return new RoundedShapeFactory();
}else{
return new ShapeFactory();
}
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class Rectangle implements Shape {
@Override
public void drawShape() {
System.out.println("Inside Rectangle::draw() method.");
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class RoundedRectangle implements Shape {
@Override
public void drawShape() {
System.out.println("Inside RoundedRectangle::draw() method.");
}
}

View File

@@ -1,16 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class RoundedShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new RoundedRectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new RoundedSquare();
}
return null;
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class RoundedSquare implements Shape {
@Override
public void drawShape() {
System.out.println("Inside RoundedSquare::draw() method.");
}
}

View File

@@ -1,8 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public interface Shape {
void drawShape();
}

View File

@@ -1,16 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory;
/**
* @author Kunwar
*/
public class Square implements Shape {
@Override
public void drawShape() {
System.out.println("Inside Square::draw() method.");
}
}

View File

@@ -1,8 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Abstract Factory - Factory of Factories
*/
public abstract class AbstractFactory{
abstract IMobile getMobile(String mobileModel) ;
}

View File

@@ -1,19 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Client Class
*/
public class AbstractFactoryPatternDemo{
public static void main(String[] args){
AbstractFactory abstractFactory1 = MpbileFactoryProducer.getFactory(false);
IMobile onePlus = abstractFactory1.getMobile("Oneplus");
onePlus.brandName();
IMobile sony = abstractFactory1.getMobile("Sony");
sony.brandName();
IMobile lava = abstractFactory1.getMobile("Lava");
lava.brandName();
AbstractFactory abstractFactory2 = MpbileFactoryProducer.getFactory(true);
IMobile iphone = abstractFactory2.getMobile("iphone");
iphone.brandName();
}
}

View File

@@ -1,18 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Android Mobile Factory
*/
public class AndroidMobileFactory extends AbstractFactory {
@Override
public IMobile getMobile(String mobileModel){
if(mobileModel.equalsIgnoreCase("Oneplus")){
return new OnePlus();
}else if(mobileModel.equalsIgnoreCase("Sony")){
return new Sony();
}else if(mobileModel.equalsIgnoreCase("Lava")){
return new Lava();
}
return null;
}
}

View File

@@ -1,14 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Apple Mobile Factory
*/
public class AppleMobileFactory extends AbstractFactory{
@Override
public IMobile getMobile(String mobileModel){
if(mobileModel.equalsIgnoreCase("iphone")){
return new Iphone();
}
return null;
}
}

View File

@@ -1,8 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Mobile
*/
public interface IMobile {
void brandName();
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Iphone Mobile
*/
public class Iphone implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is Iphone");
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Nokia Mobile Concrete Class
*/
public class Lava implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is Lava");
}
}

View File

@@ -1,15 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Factory Producer
*/
public class MpbileFactoryProducer {
public static AbstractFactory getFactory(boolean isApple){
if(isApple){
return new AppleMobileFactory();
}else{
return new AndroidMobileFactory();
}
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* OnePlus Mobile
*/
public class OnePlus implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is OnePlus");
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.abstractfactory.example2;
/**
* Sony Mobile
*/
public class Sony implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is Sony");
}
}

View File

@@ -1,26 +0,0 @@
package javadevjournal.design.creational.builder;
/**
* @author Kunwar
* Builder Pattern Client Class
* Create Computer object with mandatory and optional properties
*/
public class BuilderPatternDemo {
public static void main(String[] args) {
Computer model1 = new Computer.ComputerBuilder(
"1 TB", "16 GB","15.6").setBluetoothEnabled(true)
.setGraphicsCardEnabled(true).setTouchScreenEnabled(true).setWebCamEnabled(true).build();
System.out.println("model1: " + model1.toString());
Computer model2 = new Computer.ComputerBuilder(
"256 GB", "8 GB","14.6").setBluetoothEnabled(true)
.setGraphicsCardEnabled(true).build();
System.out.println("model2: "+model2.toString());
Computer model3 = new Computer.ComputerBuilder(
"128 GB", "4 GB","13.6").build();
System.out.println("model3: "+model3.toString());
}
}

View File

@@ -1,116 +0,0 @@
package javadevjournal.design.creational.builder;
/**
* @author Kunwar
* Product Class
* Builder Pattern Class
*/
public class Computer {
//required parameters
private String HDD;
private String RAM;
private String screenSize;
//optional parameters
private boolean isGraphicsCardEnabled;
private boolean isBluetoothEnabled;
private boolean isWebCamEnabled;
private boolean isTouchScreenEnabled;
public String getHDD() {
return HDD;
}
public String getRAM() {
return RAM;
}
public String getScreenSize() {
return screenSize;
}
public boolean isGraphicsCardEnabled() {
return isGraphicsCardEnabled;
}
public boolean isBluetoothEnabled() {
return isBluetoothEnabled;
}
public boolean isWebCamEnabled() {
return isWebCamEnabled;
}
public boolean isTouchScreenEnabled() {
return isTouchScreenEnabled;
}
private Computer(ComputerBuilder builder) {
this.HDD=builder.HDD;
this.RAM=builder.RAM;
this.screenSize=builder.screenSize;
this.isGraphicsCardEnabled=builder.isGraphicsCardEnabled;
this.isBluetoothEnabled=builder.isBluetoothEnabled;
this.isTouchScreenEnabled=builder.isTouchScreenEnabled;
this.isWebCamEnabled=builder.isWebCamEnabled;
this.isWebCamEnabled=builder.isWebCamEnabled;
}
//Builder Class
public static class ComputerBuilder{
// required parameters
private String HDD;
private String RAM;
private String screenSize;
// optional parameters
private boolean isGraphicsCardEnabled;
private boolean isBluetoothEnabled;
private boolean isWebCamEnabled;
private boolean isTouchScreenEnabled;
public ComputerBuilder(String hdd, String ram, String screenSize){
this.HDD=hdd;
this.RAM=ram;
this.screenSize=screenSize;
}
public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
this.isGraphicsCardEnabled = isGraphicsCardEnabled;
return this;
}
public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
this.isBluetoothEnabled = isBluetoothEnabled;
return this;
}
public ComputerBuilder setWebCamEnabled(boolean webCamEnabled) {
isWebCamEnabled = webCamEnabled;
return this;
}
public ComputerBuilder setTouchScreenEnabled(boolean touchScreenEnabled) {
isTouchScreenEnabled = touchScreenEnabled;
return this;
}
public Computer build(){
return new Computer(this);
}
}
@Override
public String toString() {
return "Computer{" +
"HDD='" + HDD + '\'' +
", RAM='" + RAM + '\'' +
", screenSize='" + screenSize + '\'' +
", isGraphicsCardEnabled=" + isGraphicsCardEnabled +
", isBluetoothEnabled=" + isBluetoothEnabled +
", isWebCamEnabled=" + isWebCamEnabled +
", isTouchScreenEnabled=" + isTouchScreenEnabled +
'}';
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.factory;
/**
* @author Kunwar
*/
public class Circle implements Shape {
@Override
public void drawShape() {
System.out.println("Inside Circle::drawShape() method.");
}
}

View File

@@ -1,23 +0,0 @@
package javadevjournal.design.creational.factory;
/**
* @author Kunwar
* Factory Pattern Demo
*/
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
/* get an object of Circle Class and call its drawShape method. */
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.drawShape();
/* get an object of Rectangle Class and call its drawShape method. */
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.drawShape();
/* get an object of Square Class and call its drawShape method. */
Shape shape3 = shapeFactory.getShape("SQUARE");
shape3.drawShape();
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.factory;
/**
* @author Kunwar
*/
public class Rectangle implements Shape {
@Override
public void drawShape() {
System.out.println("Inside Rectangle::drawShape() method.");
}
}

View File

@@ -1,8 +0,0 @@
package javadevjournal.design.creational.factory;
/**
* @author Kunwar
*/
public interface Shape {
void drawShape();
}

View File

@@ -1,28 +0,0 @@
package javadevjournal.design.creational.factory;
/**
* @author Kunwar
* Factory Pattern Implementation here
*/
public class ShapeFactory {
/**
* get the shapeType from caller and decide the correct implementation class
* @param shapeType
* @return
*/
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.factory;
/**
* @author Kunwar
*/
public class Square implements Shape {
@Override
public void drawShape() {
System.out.println("Inside Square::drawShape() method.");
}
}

View File

@@ -1,8 +0,0 @@
package javadevjournal.design.creational.factory.banking;
/**
* @author Kunwar
*/
public interface BankAccount {
public void registerAccount();
}

View File

@@ -1,20 +0,0 @@
package javadevjournal.design.creational.factory.banking;
/**
* @author Kunwar
*/
public class BankAccountFactory {
public BankAccount createAccount(String type){
BankAccount bankAccount = null;
if (type.equals("P")){
bankAccount = new PersonalAccount();
} else if (type.equals("B")){
bankAccount = new BusinessAccount();
} else if (type.equals("C")){
bankAccount = new CheckingAccount();
} else {
System.out.println("Invalid type");
}
return bankAccount;
}
}

View File

@@ -1,22 +0,0 @@
package javadevjournal.design.creational.factory.banking;
import java.util.Scanner;
/**
* @author Kunwar
*/
public class Branch {
public static void main(String[] args) {
BankAccount bankAccount = null;
BankAccountFactory bankAccountFactory = new BankAccountFactory();
Scanner in = new Scanner(System.in);
System.out.println("Please enter\n" +
" P for Personal account\n" +
" B for Business account\n" +
" C for Checking account\n" +
"----------------------------");
String accountType = in.nextLine();
bankAccount = bankAccountFactory.createAccount(accountType);
bankAccount.registerAccount();;
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.factory.banking;
/**
* @author Kunwar
*/
public class BusinessAccount implements BankAccount {
@Override
public void registerAccount() {
System.out.println("Creating a business account");
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.factory.banking;
/**
* @author Kunwar
*/
public class CheckingAccount implements BankAccount{
@Override
public void registerAccount() {
System.out.println("Creating a checking account");
}
}

View File

@@ -1,11 +0,0 @@
package javadevjournal.design.creational.factory.banking;
/**
* @author Kunwar
*/
public class PersonalAccount implements BankAccount{
@Override
public void registerAccount() {
System.out.println("Creating a personal account");
}
}

View File

@@ -1,18 +0,0 @@
package javadevjournal.design.creational.singleton;
/**
* @author Kunwar
*/
public class Singleton {
/* private instance variable */
private static Singleton instance = new Singleton();
/* private constructor */
private Singleton(){}
/* returns the same object */
public static Singleton getInstance(){
return instance;
}
}

View File

@@ -1,23 +0,0 @@
package javadevjournal.design.creational.singleton;
public class SingletonMultiThreaded {
/* private instance variable */
private static volatile SingletonMultiThreaded INSTANCE;
/* private constructor */
private SingletonMultiThreaded() { }
public static SingletonMultiThreaded getInstance() {
/* double-checking lock */
if(null == INSTANCE){
/* synchronized block */
synchronized (SingletonMultiThreaded.class) {
if(null == INSTANCE){
INSTANCE = new SingletonMultiThreaded();
}
}
}
return INSTANCE;
}
}

View File

@@ -1,24 +0,0 @@
package javadevjournal.design.creational.singleton;
/**
* @author Kunwar
*/
public class SingletonPatternDemo {
public static void main(String[] args) {
/* Let's create 3 objects and see their hashcode and they will be same. */
System.out.println("in single threaded environment");
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
Singleton singleton3 = Singleton.getInstance();
System.out.println(singleton1.hashCode() +" "+ singleton2.hashCode() +" "+ singleton3.hashCode());
System.out.println("in multi threaded environment");
Thread1 t1 = new Thread1();
t1.run();
Thread2 t2 = new Thread2();
t2.run();
Thread3 t3 = new Thread3();
t3.run();
}
}

View File

@@ -1,9 +0,0 @@
package javadevjournal.design.creational.singleton;
public class Thread1 implements Runnable{
@Override
public void run() {
SingletonMultiThreaded singletonMultiThreaded = SingletonMultiThreaded.getInstance();
System.out.print(singletonMultiThreaded.hashCode() + " ");
}
}

View File

@@ -1,9 +0,0 @@
package javadevjournal.design.creational.singleton;
public class Thread2 implements Runnable{
@Override
public void run() {
SingletonMultiThreaded singletonMultiThreaded = SingletonMultiThreaded.getInstance();
System.out.print(singletonMultiThreaded.hashCode() + " ");
}
}

View File

@@ -1,9 +0,0 @@
package javadevjournal.design.creational.singleton;
public class Thread3 implements Runnable{
@Override
public void run() {
SingletonMultiThreaded singletonMultiThreaded = SingletonMultiThreaded.getInstance();
System.out.println(singletonMultiThreaded.hashCode());
}
}

View File

@@ -1,27 +0,0 @@
package javadevjournal.design.creational.singleton.breakit;
import java.lang.reflect.Constructor;
/**
* @author Kunwar
*/
public class BreakSingletonUsingReflection {
public static void main(String[] args) {
Singleton singleton1 = Singleton.instance;
Singleton singleton2 = null;
try {
Constructor[] constructors =
Singleton.class.getDeclaredConstructors();
for (Constructor constructor : constructors) {
// Below code will destroy the singleton pattern
constructor.setAccessible(true);
singleton2 = (Singleton) constructor.newInstance();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("instance1.hashCode(): " + singleton1.hashCode());
System.out.println("instance2.hashCode(): " + singleton2.hashCode());
}
}

View File

@@ -1,10 +0,0 @@
package javadevjournal.design.creational.singleton.breakit;
/**
* @author Kunwar
*/
class Singleton{
public static Singleton instance = new Singleton();
private Singleton() {
}
}

View File

@@ -1,14 +0,0 @@
package javadevjournal.design.structural.adapter;
/**
* @author Kunwar
*/
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.playMusic("mp3", "song1.mp3");
audioPlayer.playMusic("mp4", "song2.mp4");
audioPlayer.playMusic("vlc", "song3.vlc");
audioPlayer.playMusic("xyz", "song4.avi");
}
}

View File

@@ -1,9 +0,0 @@
package javadevjournal.design.structural.adapter;
/**
* @author Kunwar
*/
public interface AdvancedMediaPlayer {
void playVlcPlayer(String fileName);
void playMp4Player(String fileName);
}

View File

@@ -1,22 +0,0 @@
package javadevjournal.design.structural.adapter;
/**
* @author Kunwar
*/
public class AudioPlayer implements MediaPlayer {
private MediaAdapter mediaAdapter;
@Override
public void playMusic(String audioType, String fileName) {
//the mp3 format is supported by AudioPlayer itself and it doesn't need adapter here.
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file: " + fileName);
}
//to support other formats, we will need the MediaAdapter
else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.playMusic(audioType, fileName);
}else{
System.out.println("The given format: " + audioType + " is not supported");
}
}
}

View File

@@ -1,28 +0,0 @@
package javadevjournal.design.structural.adapter;
/**
* @author Kunwar
*/
public class MediaAdapter implements MediaPlayer {
public static final String VLC = "vlc";
public static final String MP_4 = "mp4";
private AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase(VLC) ){
advancedMusicPlayer = new VlcMusicPlayer();
}else if (audioType.equalsIgnoreCase(MP_4)){
advancedMusicPlayer = new Mp4MusicPlayer();
}
}
@Override
public void playMusic(String audioType, String fileName) {
if(audioType.equalsIgnoreCase(VLC)){
advancedMusicPlayer.playVlcPlayer(fileName);
}else if(audioType.equalsIgnoreCase(MP_4)){
advancedMusicPlayer.playMp4Player(fileName);
}
}
}

View File

@@ -1,8 +0,0 @@
package javadevjournal.design.structural.adapter;
/**
* @author Kunwar
*/
public interface MediaPlayer{
void playMusic(String audioType, String fileName);
}

View File

@@ -1,17 +0,0 @@
package javadevjournal.design.structural.adapter;
/**
* @author Kunwar
*/
public class Mp4MusicPlayer implements AdvancedMediaPlayer{
@Override
public void playVlcPlayer(String fileName) {
//do nothing
}
@Override
public void playMp4Player(String fileName) {
System.out.println("Playing mp4 file: "+ fileName);
}
}

View File

@@ -1,16 +0,0 @@
package javadevjournal.design.structural.adapter;
/**
* @author Kunwar
*/
public class VlcMusicPlayer implements AdvancedMediaPlayer{
@Override
public void playVlcPlayer(String fileName){
System.out.println("Playing vlc file: "+ fileName);
}
@Override
public void playMp4Player(String fileName) {
//do nothing
}
}

View File

@@ -1,22 +0,0 @@
package javadevjournal.ds.avl;
class AVLNode{
AVLNode left, right;
int data;
int height;
public AVLNode(){
left = null;
right = null;
data = 0;
height = 0;
}
public AVLNode(int n){
left = null;
right = null;
data = n;
height = 0;
}
}

View File

@@ -1,195 +0,0 @@
package javadevjournal.ds.avl;
class AVLTree{
private AVLNode root;
public AVLTree(){
root = null;
}
/**
*
* @param avlNode
* @return
*/
private int height(AVLNode avlNode ){
return avlNode == null ? -1 : avlNode.height;
}
/**
*
* @param lHeight
* @param rHeight
* @return
*/
private int max(int lHeight, int rHeight){
return lHeight > rHeight ? lHeight : rHeight;
}
/**
*
* @param data
*/
public void insert(int data){
root = insert(data, root);
}
/**
*
* @param data
* @param avlNode
* @return
*/
private AVLNode insert(int data, AVLNode avlNode)
{
if (avlNode == null)
avlNode = new AVLNode(data);
else if (data < avlNode.data){
avlNode.left = insert( data, avlNode.left );
if( height( avlNode.left ) - height( avlNode.right ) == 2 )
if( data < avlNode.left.data )
avlNode = leftRotation( avlNode );
else
avlNode = leftRightRotation( avlNode );
}
else if( data > avlNode.data ){
avlNode.right = insert( data, avlNode.right );
if( height( avlNode.right ) - height( avlNode.left ) == 2 )
if( data > avlNode.right.data)
avlNode = rightRotation( avlNode );
else
avlNode = rightLeftRotation( avlNode );
}
else
; // Duplicate; do nothing
avlNode.height = max( height( avlNode.left ), height( avlNode.right ) ) + 1;
return avlNode;
}
/**
*
* @param avlNode
* @return
*/
private AVLNode leftRotation(AVLNode avlNode){
AVLNode k1 = avlNode.left;
avlNode.left = k1.right;
k1.right = avlNode;
avlNode.height = max( height( avlNode.left ), height( avlNode.right ) ) + 1;
k1.height = max( height( k1.left ), avlNode.height ) + 1;
return k1;
}
/**
*
* @param avlNode
* @return
*/
private AVLNode rightRotation(AVLNode avlNode){
AVLNode node = avlNode.right;
avlNode.right = node.left;
node.left = avlNode;
avlNode.height = max( height( avlNode.left ), height( avlNode.right ) ) + 1;
node.height = max( height( node.right ), avlNode.height ) + 1;
return node;
}
/**
* left-right rotation
* @param avlNode
* @return
*/
private AVLNode leftRightRotation(AVLNode avlNode){
avlNode.left = rightRotation( avlNode.left );
return leftRotation( avlNode );
}
/**
* right-left rotation
* @param avlNode
* @return
*/
private AVLNode rightLeftRotation(AVLNode avlNode)
{
avlNode.right = leftRotation( avlNode.right );
return rightRotation( avlNode );
}
/**
*
* @return
*/
public int countNodes(){
return countNodes(root);
}
/**
*
* @param avlNode
* @return
*/
private int countNodes(AVLNode avlNode){
if (avlNode == null)
return 0;
else{
int l = 1;
l += countNodes(avlNode.left);
l += countNodes(avlNode.right);
return l;
}
}
/**
*
* @param data
* @return
*/
public boolean search(int data){
return search(root, data);
}
/**
*
* @param avlNode
* @param data
* @return
*/
private boolean search(AVLNode avlNode, int data){
boolean found = false;
while ((avlNode != null) && !found)
{
int rval = avlNode.data;
if (data < rval)
avlNode = avlNode.left;
else if (data > rval)
avlNode = avlNode.right;
else
{
found = true;
break;
}
found = search(avlNode, data);
}
return found;
}
/**
*
*/
public void inorder(){
inorder(root);
}
/**
*
* @param avlNode
*/
private void inorder(AVLNode avlNode){
if (avlNode != null){
inorder(avlNode.left);
System.out.print(avlNode.data +" ");
inorder(avlNode.right);
}
}
}

View File

@@ -1,42 +0,0 @@
package javadevjournal.ds.avl;
import java.util.Scanner;
public class AVLTreeHelper{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
AVLTree avlTree = new AVLTree();
char ch;
do{
System.out.println("\nAVLTree Operations\n");
System.out.println("1. insert ");
System.out.println("2. search");
System.out.println("3. count nodes");
int choice = scanner.nextInt();
switch (choice)
{
case 1 :
System.out.println("Enter integer element to insert");
avlTree.insert( scanner.nextInt() );
break;
case 2 :
System.out.println("Enter integer element to search");
System.out.println("Search result : "+ avlTree.search( scanner.nextInt()));
break;
case 3 :
System.out.println("Nodes = "+ avlTree.countNodes());
break;
default :
System.out.println("Wrong Entry \n ");
break;
}
System.out.print("\nIn order : ");
avlTree.inorder();
System.out.println("\nDo you want to continue (Type y or n) \n");
ch = scanner.next().charAt(0);
} while (ch == 'Y'|| ch == 'y');
}
}

8
java-tutorials/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -122,19 +122,31 @@
</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>-->
<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>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>
<!-- dependency for the MFA -->
<dependency>
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
<build>

View File

@@ -1,142 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-devtools:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.3" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-security:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-config:5.3.3.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.3.3.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.3.3.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.36" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.36" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.5.Final" level="project" />
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-thymeleaf:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf-spring5:3.0.11.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf:3.0.11.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.attoparser:attoparser:2.0.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.unbescape:unbescape:1.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-jpa:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: jakarta.transaction:jakarta.transaction-api:1.3.3" level="project" />
<orderEntry type="library" name="Maven: jakarta.persistence:jakarta.persistence-api:2.2.3" level="project" />
<orderEntry type="library" name="Maven: org.hibernate:hibernate-core:5.4.17.Final" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.24.0-GA" level="project" />
<orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.11" level="project" />
<orderEntry type="library" name="Maven: antlr:antlr:2.7.7" level="project" />
<orderEntry type="library" name="Maven: org.jboss:jandex:2.1.3.Final" level="project" />
<orderEntry type="library" name="Maven: org.dom4j:dom4j:2.1.3" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.common:hibernate-commons-annotations:5.1.0.Final" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:jaxb-runtime:2.3.3" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:txw2:2.3.3" level="project" />
<orderEntry type="library" name="Maven: com.sun.istack:istack-commons-runtime:3.0.11" level="project" />
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-jpa:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-orm:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aspects:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.5.1" level="project" />
<orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.12" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:8.0.18" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-mail:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.sun.mail:jakarta.mail:1.6.5" level="project" />
<orderEntry type="library" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.11" level="project" />
<orderEntry type="library" name="Maven: org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.springframework.session:spring-session-data-redis:2.3.0.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.7.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.session:spring-session-core:2.3.0.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.3.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.50.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.50.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.50.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.50.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.50.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.50.Final" level="project" />
<orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:2.3.1.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:2.3.1.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:2.3.1.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.4.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:1.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
<orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
<orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.16.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest:2.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.6.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.6.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.6.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-params:5.6.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.6.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.6.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:3.3.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.10.11" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:2.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-junit-jupiter:3.3.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.5.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.2.7.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.7.0" level="project" />
</component>
</module>

View File

@@ -1,5 +1,14 @@
package com.javadevjournal.config;
import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import dev.samstevens.totp.code.DefaultCodeVerifier;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.qr.ZxingPngQrGenerator;
import dev.samstevens.totp.secret.DefaultSecretGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.time.SystemTimeProvider;
import dev.samstevens.totp.time.TimeProvider;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -68,4 +77,20 @@ public class AppConfig implements WebMvcConfigurer {
}
// setting up thymeleaf for Spring Email TemplateEngine
@Bean
public SecretGenerator secretGenerator(){
return new DefaultSecretGenerator(64);
}
@Bean
public QrGenerator qrGenerator(){
return new ZxingPngQrGenerator();
}
@Bean
public CodeVerifier codeVerifier(){
return new DefaultCodeVerifier(new DefaultCodeGenerator(), new SystemTimeProvider());
}
}

View File

@@ -2,36 +2,28 @@ 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 com.javadevjournal.core.security.web.authentication.CustomWebAuthenticationDetailsSource;
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.AuthenticationManager;
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;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
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;
@@ -47,14 +39,17 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
UserDetailsService userDetailsService;
@Autowired
@Resource
PasswordEncoder passwordEncoder;
@Autowired
@Resource
private DataSource dataSource;
@Resource
CustomAuthenticationProvider customAuthenticationProvider;
private CustomAuthenticationProvider customAuthenticationProvider;
@Resource
private CustomWebAuthenticationDetailsSource authenticationDetailsSource;
@Override
protected void configure(HttpSecurity http) throws Exception {
@@ -68,7 +63,6 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
//Setting HTTPS for my account
.requiresChannel().anyRequest().requiresSecure()
.and()
// Remember me configurations
.rememberMe().tokenRepository(persistentTokenRepository())
.rememberMeCookieDomain("domain")
@@ -85,6 +79,7 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
.failureUrl("/login?error=true")
.successHandler(successHandler())
.failureHandler(failureHandler())
.authenticationDetailsSource(authenticationDetailsSource) //injecting custom authenitcation source
//logout configurations
.and()
.logout().deleteCookies("dummyCookie")
@@ -97,15 +92,16 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
http.authorizeRequests().antMatchers("/admim/**").hasAuthority("ADMIN");
http.addFilterAfter(customHeaderAuthFilter(), UsernamePasswordAuthenticationFilter.class);
//http.addFilterAfter(customHeaderAuthFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
/* @Bean
public CustomHeaderAuthFilter customHeaderAuthFilter(){
return new CustomHeaderAuthFilter();
}
} */
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
@@ -138,14 +134,14 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
return new CustomAccessDeniedHandler();
}
@Bean
/* @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(){
@@ -188,14 +184,13 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
* DAO authentication provider. This authentication provider will authenticate the user with the help of
* @UserdetailsService. This is based on the validating the user with the username and password.
* @return
*/
@Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(passwordEncoder);
public CustomAuthenticationProvider authProvider() {
CustomAuthenticationProvider authenticationProvider = new CustomAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
return authenticationProvider;
}
} */
/**
* Authentication manager which will be invoked by Spring security filter chain. This authentication
@@ -206,11 +201,11 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
*/
@Override
protected void configure(AuthenticationManagerBuilder auth){
//auth.authenticationProvider(authProvider());
auth.authenticationProvider(customAuthenticationProvider)
.authenticationProvider(authProvider());
auth.authenticationProvider(customAuthenticationProvider);
//auth.authenticationProvider(customAuthenticationProvider).authenticationProvider(authProvider());
}
/**
* Using this to persist the remember-me token in the database for more secure approach.
* We are not usin gthe memory based remember-me cookie which is not very secure but saving the token in the
@@ -226,6 +221,7 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
//Spring security LDAP configurations
/*
@Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
@@ -237,5 +233,5 @@ public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
} */
}

View File

@@ -1,50 +1,51 @@
package com.javadevjournal.core.security.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import com.javadevjournal.core.security.core.userdetail.CustomUser;
import com.javadevjournal.core.security.mfa.MFATokenManager;
import com.javadevjournal.core.security.web.authentication.CustomWebAuthenticationDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
public class CustomAuthenticationProvider extends DaoAuthenticationProvider {
@Resource
UserDetailsService userDetailsService;
private MFATokenManager mfaTokenManager;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
@Resource
private PasswordEncoder passwordEncoder;
if (StringUtils.isEmpty(username)) {
throw new BadCredentialsException("invalid login details");
@Autowired
public CustomAuthenticationProvider(UserDetailsService userDetailsService) {
super.setUserDetailsService(userDetailsService);
}
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
super.additionalAuthenticationChecks(userDetails, authentication);
//token check
CustomWebAuthenticationDetails authenticationDetails = (CustomWebAuthenticationDetails) authentication.getDetails();
CustomUser user = (CustomUser) userDetails;
String mfaToken = authenticationDetails.getToken();
if(!mfaTokenManager.verifyTotp(mfaToken,user.getSecret())){
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
// 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

@@ -13,21 +13,22 @@ import java.util.function.Function;
public class CustomUser implements UserDetails {
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
private String username;
private Collection<GrantedAuthority> authorities;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;
private String secret;
public CustomUser(String username, String password,
Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
Collection<? extends GrantedAuthority> authorities, String secret) {
this(username, password, true, true, true, true, authorities, secret);
}
public CustomUser(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities, final String secret) {
if (((username == null) || "".equals(username)) || (password == null)) {
throw new IllegalArgumentException(
@@ -41,6 +42,7 @@ public class CustomUser implements UserDetails {
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = null;
this.secret= secret;
}
@@ -76,6 +78,10 @@ public class CustomUser implements UserDetails {
password = null;
}
public String getSecret() {
return secret;
}
@Override
public boolean equals(Object rhs) {
if (rhs instanceof CustomUser) {
@@ -89,209 +95,68 @@ public class CustomUser implements UserDetails {
return username.hashCode();
}
public static CustomUser.UserBuilder withUsername(String username) {
return builder().username(username);
}
public static CustomUser.UserBuilder builder() {
return new CustomUser.UserBuilder();
}
public static CustomUser.UserBuilder withUserDetails(UserDetails userDetails) {
return withUsername(userDetails.getUsername())
.password(userDetails.getPassword())
.accountExpired(!userDetails.isAccountNonExpired())
.accountLocked(!userDetails.isAccountNonLocked())
.authorities(userDetails.getAuthorities())
.credentialsExpired(!userDetails.isCredentialsNonExpired())
.disabled(!userDetails.isEnabled());
}
public static class UserBuilder {
private String username;
public static final class CustomUserBuilder {
private String password;
private List<GrantedAuthority> authorities;
private boolean accountExpired;
private boolean accountLocked;
private boolean credentialsExpired;
private boolean disabled;
private Function<String, String> passwordEncoder = password -> password;
private String username;
private Collection<GrantedAuthority> authorities;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;
private String secret;
/**
* Creates a new instance
*/
private UserBuilder() {
private CustomUserBuilder() {
}
/**
* Populates the username. This attribute is required.
*
* @param username the username. Cannot be null.
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder username(String username) {
Assert.notNull(username, "username cannot be null");
this.username = username;
return this;
public static CustomUserBuilder aCustomUser() {
return new CustomUserBuilder();
}
/**
* Populates the password. This attribute is required.
*
* @param password the password. Cannot be null.
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder password(String password) {
Assert.notNull(password, "password cannot be null");
public CustomUserBuilder withPassword(String password) {
this.password = password;
return this;
}
/**
* Encodes the current password (if non-null) and any future passwords supplied
* to {@link #password(String)}.
*
* @param encoder the encoder to use
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder passwordEncoder(Function<String, String> encoder) {
Assert.notNull(encoder, "encoder cannot be null");
this.passwordEncoder = encoder;
public CustomUserBuilder withUsername(String username) {
this.username = username;
return this;
}
/**
* Populates the roles. This method is a shortcut for calling
* {@link #authorities(String...)}, but automatically prefixes each entry with
* "ROLE_". This means the following:
*
* <code>
* builder.roles("USER","ADMIN");
* </code>
*
* is equivalent to
*
* <code>
* builder.authorities("ROLE_USER","ROLE_ADMIN");
* </code>
*
* <p>
* This attribute is required, but can also be populated with
* {@link #authorities(String...)}.
* </p>
*
* @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,
* contain null values or start with "ROLE_"
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder roles(String... roles) {
List<GrantedAuthority> authorities = new ArrayList<>(
roles.length);
for (String role : roles) {
Assert.isTrue(!role.startsWith("ROLE_"), () -> role
+ " cannot start with ROLE_ (it is automatically added)");
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
}
return authorities(authorities);
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public CustomUser.UserBuilder authorities(GrantedAuthority... authorities) {
return authorities(Arrays.asList(authorities));
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public CustomUser.UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = new ArrayList<>(authorities);
public CustomUserBuilder withAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
return this;
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,
* etc). Cannot be null, or contain null values
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public CustomUser.UserBuilder authorities(String... authorities) {
return authorities(AuthorityUtils.createAuthorityList(authorities));
}
/**
* Defines if the account is expired or not. Default is false.
*
* @param accountExpired true if the account is expired, false otherwise
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder accountExpired(boolean accountExpired) {
this.accountExpired = accountExpired;
public CustomUserBuilder withAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
return this;
}
/**
* Defines if the account is locked or not. Default is false.
*
* @param accountLocked true if the account is locked, false otherwise
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder accountLocked(boolean accountLocked) {
this.accountLocked = accountLocked;
public CustomUserBuilder withAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
return this;
}
/**
* Defines if the credentials are expired or not. Default is false.
*
* @param credentialsExpired true if the credentials are expired, false otherwise
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder credentialsExpired(boolean credentialsExpired) {
this.credentialsExpired = credentialsExpired;
public CustomUserBuilder withCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
return this;
}
/**
* Defines if the account is disabled or not. Default is false.
*
* @param disabled true if the account is disabled, false otherwise
* @return the {@link User.UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public CustomUser.UserBuilder disabled(boolean disabled) {
this.disabled = disabled;
public CustomUserBuilder withEnabled(boolean enabled) {
this.enabled = enabled;
return this;
}
public UserDetails build() {
String encodedPassword = this.passwordEncoder.apply(password);
return new CustomUser(username, encodedPassword, !disabled, !accountExpired,
!credentialsExpired, !accountLocked, authorities);
public CustomUserBuilder withSecret(String secret) {
this.secret = secret;
return this;
}
public CustomUser build() {
CustomUser customUser = new CustomUser(username, password, !enabled, !accountNonExpired, !credentialsNonExpired, !accountNonLocked, authorities, secret);
customUser.authorities = this.authorities;
return customUser;
}
}
}

View File

@@ -28,26 +28,21 @@ public class CustomUserDetailService implements UserDetailsService{
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
//Let's split the email to get both values
String[] usernameAndCustomToken = StringUtils.split(email, String.valueOf(Character.LINE_SEPARATOR));
public CustomUser loadUserByUsername(String email) throws UsernameNotFoundException {
//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);
final UserEntity customer = userRepository.findByEmail(email);
if (customer == null) {
throw new UsernameNotFoundException(email);
}
boolean enabled = !customer.isAccountVerified(); // we can use this in case we want to activate account after customer verified the account
UserDetails user = User.withUsername(customer.getEmail())
.password(customer.getPassword())
.disabled(customer.isLoginDisabled())
.authorities(getAuthorities(customer)).build()
;
CustomUser user = CustomUser.CustomUserBuilder.aCustomUser().
withUsername(customer.getEmail())
.withPassword(customer.getPassword())
.withEnabled(customer.isLoginDisabled())
.withAuthorities(getAuthorities(customer))
.withSecret(customer.getSecret())
.withAccountNonLocked(false)
.build();
return user;
}

View File

@@ -18,8 +18,9 @@ public class CustomHeaderAuthFilter extends GenericFilterBean {
var request = (HttpServletRequest) servletRequest;
var response = (HttpServletResponse)servletResponse;
filterChain.doFilter(servletRequest, servletResponse);
//if header is missing , send un-athorized error back
/*
String authHeader = request.getHeader("X-HEADER");
if(StringUtils.isEmpty(authHeader)){
response.setStatus(
@@ -27,6 +28,6 @@ public class CustomHeaderAuthFilter extends GenericFilterBean {
}
else{
filterChain.doFilter(servletRequest, servletResponse);
}
} */
}
}

View File

@@ -0,0 +1,50 @@
package com.javadevjournal.core.security.mfa;
import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.code.HashingAlgorithm;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.util.Utils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("mfaTokenManager")
public class DefaultMFATokenManager implements MFATokenManager{
@Resource
private SecretGenerator secretGenerator;
@Resource
private QrGenerator qrGenerator;
@Resource
private CodeVerifier codeVerifier;
@Override
public String generateSecretKey() {
return secretGenerator.generate();
}
@Override
public String getQRCode(String secret) throws QrGenerationException {
QrData data = new QrData.Builder().label("MFA")
.secret(secret)
.issuer("Java Development Journal")
.algorithm(HashingAlgorithm.SHA256)
.digits(6)
.period(30)
.build();
return Utils.getDataUriForImage(
qrGenerator.generate(data),
qrGenerator.getImageMimeType()
);
}
@Override
public boolean verifyTotp(String code, String secret) {
return codeVerifier.isValidCode(secret, code);
}
}

View File

@@ -0,0 +1,10 @@
package com.javadevjournal.core.security.mfa;
import dev.samstevens.totp.exceptions.QrGenerationException;
public interface MFATokenManager {
String generateSecretKey();
String getQRCode(final String secret) throws QrGenerationException;
boolean verifyTotp(final String code, final String secret);
}

View File

@@ -7,7 +7,7 @@ import java.util.Objects;
public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
private final String token;
private String token;
/**
* Records the remote address and will also set the session Id if a session already
* exists (it won't create one).
@@ -40,4 +40,12 @@ public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
public int hashCode() {
return Objects.hash(super.hashCode(), token);
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}

View File

@@ -0,0 +1,16 @@
package com.javadevjournal.core.security.web.authentication;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class CustomWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
@Override
public CustomWebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new CustomWebAuthenticationDetails(context);
}
}

View File

@@ -25,6 +25,9 @@ public class UserEntity {
private int failedLoginAttempts;
private boolean loginDisabled;
private boolean mfaEnabled;
private String secret;
@OneToMany(mappedBy = "user")
private Set<SecureToken> tokens;
@@ -128,6 +131,22 @@ public class UserEntity {
this.loginDisabled = loginDisabled;
}
public boolean isMfaEnabled() {
return mfaEnabled;
}
public void setMfaEnabled(boolean mfaEnabled) {
this.mfaEnabled = mfaEnabled;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -6,13 +6,16 @@ import com.javadevjournal.core.exception.InvalidTokenException;
import com.javadevjournal.core.exception.UnkownIdentifierException;
import com.javadevjournal.core.exception.UserAlreadyExistException;
import com.javadevjournal.core.security.jpa.SecureToken;
import com.javadevjournal.core.security.mfa.MFATokenManager;
import com.javadevjournal.core.security.token.SecureTokenService;
import com.javadevjournal.core.security.token.repository.SecureTokenRepository;
import com.javadevjournal.core.user.jpa.data.Group;
import com.javadevjournal.core.user.jpa.data.UserEntity;
import com.javadevjournal.core.user.jpa.repository.UserGroupRepository;
import com.javadevjournal.core.user.jpa.repository.UserRepository;
import com.javadevjournal.web.data.user.MfaTokenData;
import com.javadevjournal.web.data.user.UserData;
import dev.samstevens.totp.exceptions.QrGenerationException;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@@ -21,33 +24,37 @@ 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;
@Service("userService")
public class DefaultUserService implements UserService{
@Autowired
@Resource
private UserRepository userRepository;
@Autowired
@Resource
private PasswordEncoder passwordEncoder;
@Autowired
@Resource
private EmailService emailService;
@Autowired
@Resource
private SecureTokenService secureTokenService;
@Autowired
@Resource
SecureTokenRepository secureTokenRepository;
@Autowired
@Resource
UserGroupRepository groupRepository;
@Value("${site.base.url.https}")
private String baseURL;
@Resource
private MFATokenManager mfaTokenManager;
@Override
public void register(UserData user) throws UserAlreadyExistException {
if(checkIfUserExist(user.getEmail())){
@@ -57,6 +64,7 @@ public class DefaultUserService implements UserService{
BeanUtils.copyProperties(user, userEntity);
encodePassword(user, userEntity);
updateCustomerGroup(userEntity);
userEntity.setSecret(mfaTokenManager.generateSecretKey());
userRepository.save(userEntity);
sendRegistrationConfirmationEmail(userEntity);
@@ -67,6 +75,7 @@ public class DefaultUserService implements UserService{
userEntity.addUserGroups(group);
}
@Override
public boolean checkIfUserExist(String email) {
return userRepository.findByEmail(email)!=null ? true : false;
@@ -83,7 +92,7 @@ public class DefaultUserService implements UserService{
emailContext.buildVerificationUrl(baseURL, secureToken.getToken());
try {
emailService.sendMail(emailContext);
} catch (MessagingException e) {
} catch (Exception e) {
e.printStackTrace();
}
@@ -117,6 +126,16 @@ public class DefaultUserService implements UserService{
return user;
}
@Override
public MfaTokenData mfaSetup(String email) throws UnkownIdentifierException, QrGenerationException {
UserEntity user= userRepository.findByEmail(email);
if(user == null ){
// we will ignore in case account is not verified or account does not exists
throw new UnkownIdentifierException("unable to find account or account is not active");
}
return new MfaTokenData( mfaTokenManager.getQRCode( user.getSecret()), user.getSecret());
}
private void encodePassword(UserData source, UserEntity target){
target.setPassword(passwordEncoder.encode(source.getPassword()));
}

View File

@@ -4,7 +4,9 @@ import com.javadevjournal.core.exception.InvalidTokenException;
import com.javadevjournal.core.exception.UnkownIdentifierException;
import com.javadevjournal.core.exception.UserAlreadyExistException;
import com.javadevjournal.core.user.jpa.data.UserEntity;
import com.javadevjournal.web.data.user.MfaTokenData;
import com.javadevjournal.web.data.user.UserData;
import dev.samstevens.totp.exceptions.QrGenerationException;
public interface UserService {
@@ -13,4 +15,5 @@ public interface UserService {
void sendRegistrationConfirmationEmail(final UserEntity user);
boolean verifyUser(final String token) throws InvalidTokenException;
UserEntity getUserById(final String id) throws UnkownIdentifierException;
MfaTokenData mfaSetup(final String email) throws UnkownIdentifierException, QrGenerationException;
}

View File

@@ -1,9 +1,12 @@
package com.javadevjournal.web.controller.user;
import com.javadevjournal.core.exception.InvalidTokenException;
import com.javadevjournal.core.exception.UnkownIdentifierException;
import com.javadevjournal.core.exception.UserAlreadyExistException;
import com.javadevjournal.core.user.service.UserService;
import com.javadevjournal.web.data.user.MfaTokenData;
import com.javadevjournal.web.data.user.UserData;
import dev.samstevens.totp.exceptions.QrGenerationException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
@@ -17,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.annotation.Resource;
import javax.validation.Valid;
import static com.javadevjournal.ApplicationConstant.REDIRECT;
@@ -27,10 +31,10 @@ public class RegistrationController {
private static final String REDIRECT_LOGIN= "redirect:/login";
@Autowired
@Resource
private UserService userService;
@Autowired
@Resource
private MessageSource messageSource;
@GetMapping
@@ -47,7 +51,11 @@ public class RegistrationController {
}
try {
userService.register(userData);
}catch (UserAlreadyExistException e){
MfaTokenData mfaData = userService.mfaSetup(userData.getEmail());
model.addAttribute("qrCode", mfaData.getQrCode());
model.addAttribute("qrCodeKey", mfaData.getMfaCode());
model.addAttribute("qrCodeSetup", true);
}catch (UserAlreadyExistException | QrGenerationException | UnkownIdentifierException e){
bindingResult.rejectValue("email", "userData.email","An account already exists for this email.");
model.addAttribute("registrationForm", userData);
return "account/register";

View File

@@ -0,0 +1,33 @@
package com.javadevjournal.web.data.user;
import java.io.Serializable;
public class MfaTokenData implements Serializable {
private String qrCode;
private String mfaCode;
public MfaTokenData() {
}
public String getQrCode() {
return qrCode;
}
public void setQrCode(String qrCode) {
this.qrCode = qrCode;
}
public String getMfaCode() {
return mfaCode;
}
public void setMfaCode(String mfaCode) {
this.mfaCode = mfaCode;
}
public MfaTokenData(String qrCode, String mfaCode) {
this.qrCode = qrCode;
this.mfaCode = mfaCode;
}
}

View File

@@ -63,3 +63,9 @@ jdj.security.failedlogin.count =2
jdj.brute.force.cache.max=1000
###### Spring LDAP configuration. This is using Apache DS server but you can use it for any other LDAP server
#spring.ldap.embedded.ldif=classpath:users.ldif
#spring.ldap.embedded.base-dn=dc=springframework,dc=org
#spring.ldap.embedded.port=53389

View File

@@ -9,7 +9,7 @@ login.error= Username or password is incorrect. Please make sure to provide vali
user.registration.verification.missing.token= Token is empty. Please make sure to copy the entire URL
user.registration.verification.invalid.token= It seems that token is expired or has been modified.Please provide a valid token.
user.registration.verification.success= Thanks for the account verification. You can now login to your account.
user.registration.verification.email.msg= Thanks for your registration. We have sent a verification email. Please verify your account.
user.registration.verification.email.msg= Thanks for your registration. We have sent a verification email. Please verify your account.Please scan the QR code for generating MFA token for login.
user.forgotpwd.msg= If the email address entered matches your account, you will receive an email with a link to reset your password.
user.password.updated.msg= Your password is updated. Please login
user.account.locked = Your account has been locked due to multiple failed login attempts.

View File

@@ -18,10 +18,16 @@
</div>
</div>
<div class="card">
<div class="card-body register-card-body">
<div class="alert alert-success" role="alert" th:if="${registrationMsg != null}">
<p th:text="${registrationMsg}"></p>
</div>
<div th:if="${registrationMsg != null}">
<img class="col-md-12" th:src="${qrCode}" />
<p th:text="${qrCodeKey}"></p>
</div>
<div th:if="${registrationMsg == null}">
<p class="login-box-msg">Register a new account</p>
<form action="#" th:action="@{/register}" th:object="${userData}" method="post">
<div class="alert alert-danger" th:if="${#fields.hasErrors('*')}">
@@ -90,7 +96,7 @@
<!-- /.col -->
</div>
</form>
</div>
<a href="login" class="text-center">I already have an Account</a>
</div>
<!-- /.form-box -->