Little Big Extra http://littlebigextra.com A technology blog covering topics on Java, Scala, Docker, AWS, BigData, DevOps and much more to come. Do it yourself instructions for complex to simple problems for a novice to an expert. Mon, 09 Sep 2019 11:29:17 +0000 en-US hourly 1 https://wordpress.org/?v=5.6.7 http://littlebigextra.com/wp-content/uploads/2023/04/cropped-logo-32x32.png Little Big Extra http://littlebigextra.com 32 32 How I got Tier-1 Exceptional Talent Visa and how can you get it too. http://littlebigextra.com/how-i-got-tier-1-exceptional-talent-visa/ http://littlebigextra.com/how-i-got-tier-1-exceptional-talent-visa/#comments Wed, 27 Mar 2019 15:08:36 +0000 http://www.littlebigextra.com/?p=1367 Introduction Recently, I was granted the Tier-1 Exceptional Talent visa by Home Office UK and many people have asked me how I got this visa and if they qualify for it. Please note that I am not an immigration expert and this is just a personal experience and not professional advice of any sort. As […]

The post How I got Tier-1 Exceptional Talent Visa and how can you get it too. appeared first on Little Big Extra.

]]>
Introduction

Recently, I was granted the Tier-1 Exceptional Talent visa by Home Office UK and many people have asked me how I got this visa and if they qualify for it. Please note that I am not an immigration expert and this is just a personal experience and not professional advice of any sort. As I  have been working in the Digital sector for over a decade, I applied through Tech nation and  I am just sharing my experience for stage-1 of this visa which is applying to Tech Nation.

Why did I apply for this Visa?

If you know about Tier-1 Exceptional talent visa, you might be familiar that unlike any other visa program this is the one of most coveted yet most desirable visa available. On this visa, you can start your own company, work for any employer, renew this visa as many times as you want and will also be eligible for Indefinite Leave to Remain in the UK after 3/5 years depending on which visa (Talent/Promise) has been given.

However, after the HSMP/Tier-1 programme was closed back in 2011(I believe) the visa routes have become narrower and challenging. Being in the UK for nearly a decade has helped me to get deep insight into UK Business, Technology and culture. I wanted to contribute to the UK economy by the knowledge I have gained during these years and hence I thought of applying for this visa.

Is this Visa for me?

The first thing which you will need to do is go to – and have a read in detail and see if you are a good fit. I am sure by the name of this visa type and the list of documentation needed you may feel a bit overwhelmed and might get a thought that this visa isn’t for you or you won’t qualify for it. My strong advice is to read the documentation at least 2-3 times and let it sink in. This way you might be able to identify the gaps between what is required and what you have to offer and what needs to be worked upon.

What documentation do I need?

All the documentation you need for this visa is available on  web site (check for latest documentation), you will need to keep in mind that this is a subjective visa and documents which may have worked for one person may not work for others. It is difficult to even advise someone on what exactly the documents are required. However, you will need at least

  • 2 recommendation letters from people with whom you have worked and must be in higher/authoritative position.
  • Your CV
  • At least 1 evidence which satisfies your key criteria.
  • At least 2 evidence satisfying your qualifying criteria.

You can refer to this  ( updated as of 11th March -2019, look for latest documentation if available) for more details.

What is the Hard Part?

  • Getting Recommendation letter: The most challenging part of the application, in my view, was getting the recommendations letter as you might not have worked in different companies, you might not have contacts with the higher management etc. You will need to think and find people who can vouch for your work, credentials and experience in the digital sector if they have worked along with you it will be much stronger recommendations.
  • Deciphering the vague and overlapping documentation: I found the key and qualifying criterions to be overlapping and to be the same in some regards. At the end of the day, we need to understand that the documentation is just a set of guidelines and as a candidate, you will need to use your cognizance to read between the lines. My advice would be to jot down the list of pieces of evidence that you can furnish and then arrange them in order of strongest to lightest strength. Think of all your achievements and awards and see how can they be presented as evidence and make your case stronger.
  • What to put forward as evidence: As I found the qualifying criterion to be overlapping/same I had a hard time sorting out the evidence to be put forward. The first task to identify the evidence is itself challenging and time-consuming and to put them in qualifying and key criterion is time-consuming.
  • Preparing Statement of Purpose: Well this is the crux of your application and you will need to pour your head and heart to put this document in place. I must have done some 25+ drafts versions and was editing it again and again until I was completely satisfied.

What should I do to prepare my application?

Ok, I have decided to go ahead and apply for this visa, how shall I put the application forward. How do I write my recommendation letters and statement of purpose?

  • Recommendation Letters:  When you ask the referees for recommendation letters, do clearly mention that what Tech Nation is looking for what the recommendation letter should have. In my view, the recommendation letter must cover
    • Relation of the referee to the candidate and In what capacity they know you.
    • What project/technologies were you were working on
    • How did you work had an impact on the organisation/project etc?
    • How were you different from others and how do you stand out?
    • How your skills/attitude will add value to the UK market.
  • Statement of purpose: Avoid cliches, especially the opening statement. “As long as I remember I wanted to be a computer engineer” etc. You have just 1000 words to use, so write the meaningful and impactful SOP.
    • Start with what you have done in your past and then move into your present roles how and what you are contributing.
    • Then slowly move to the future, highlighting the plan you have and how will it add value to the UK market and economy.
    • Highlight any awards, achievements and digital presence, if any.
    • Think of what extra work have you done apart from day to day job, any awards if you have received, any training you have imparted, any patents you hold etc. Take a long walk down the memory lane and see what all could be put in.
  • CV:  I have 20 years of experience and I have 20 pages of resume. If that’s the case, then throw it in the bin. No one is really interested in your projects and day to day responsibilities.
    • To make your resume stand out, slim down the projects and focus on your achievements, awards and your online social presence and impacts you have made in your career.
    • Use a one-pager resume template, keep it max to 2 pages.
  • Evidence: You can submit a max of 10 pieces of evidence, so if you have more you will need to select the top -10 strongest evidence and put them in your application.

What Pitfalls should I avoid

  • Approaching the Solicitor/Agencies: Since the stage-1 is completely based upon the statements of purpose, CV and technical evidence, its good to prepare the application yourself. Since the solicitors might not have a good purview which technical evidence holds more value than the other.
  • Eagerness to apply:  I have spoken to a couple of people, who wanted to apply for this visa as their current visa was expiring or not being happy with their current visa status etc. They all sounded eager and were ready to apply without even having proper credentials, evidence and a good recommendation. Tech nation will evaluate your application based on the documentation and evidence you provide, so take a back seat and get all the documentation sorted before application. You can apply for this visa from anywhere, anytime so even if your current visa is expiring it is ok to apply from home country.
  • Comparing yourself with others:  OK, so your friend has got this visa and now it’s your turn. Believe me, no two cases are the same and assuming because someone has got it you will automatically get it is the biggest mistake you can make.
  • Last minutes hush-hush evidence: One of the qualifying criteria is to have a strong digital presence and your contribution to the digital community. Tech nation also asks for your GITHUB and Stack-overflow profiles, creating a profile on these platforms a month before submitting the application, is not going to convince anyone that you have been contributing because of your interest. Please avoid manufacturing evidence for the sake of submitting an application, only provide genuine evidence.

Whose help should I take

There is no substitute for self-help. Reading and understanding the tech nation document will help you more than anything.

    • Tech Nation Alumni: There are a lot of people who have received this visa and are happy to help.
    • People whose application was refused: This might sound little weird but there is always a lot to learn from failures. It is important to find out why someone failed and didn’t get the visa and why you should not make the same mistake again.

What if I feel I don’t satisfy the criterion?

That is what I assume 99% of the people will feel and I also felt the same when I first heard about this visa.  I would stress the importance of reading the documentation again and getting a complete understanding.

Calm down if you feel down overwhelmed, come back after few days again to the Tech-Nation website read all the criterion carefully once again.

What if I think there is a huge gap, what shall I do?

Visa or No Visa, good profile will always help you in your career. If you have a read at the documentation you will find that Tech Nation is looking for people who have excelled in their professional journey and have been a contributor or a leader. In case you feel there is a big gap, why not work towards filling it.
Identify gaps: Identify the gap between qualifying/key criterion and then try to fill it by working towards it for e.g you already had a good idea and were thinking of applying for a patent, why not apply it now?
Increase digital presence: In the era of Social platforms, strong digital presence is a must. Contributing to digital platforms like Stack-overflow/GITHUB will strengthen your profile.
Speak up: There are a lot of social meetups which happen here and there, why not organise one or be a part of one. Any public speaking or organising experience in digital technology will help you.
Continuous Learning: IT landscape changes day by day and technologies which were hot two years ago might not be in demand anymore. It is important to keep yourself updated. Doing courses/certifications is important and is kind of proof that you have been updating yourself.

What are the Useful Tips?

Some of the tips which I would like to share are as  follows

  • If you have more than 10 evidence which you can share then use both sides of the paper, for e.g on the front page I had shown google analytics figures showing how much traffic it is generating and on the back side, I had given the number of youtube views.
  •  Patience is the key, you might need lots of time with your statement of purpose, arranging evidence. For documentation at least give yourself 2-3 months of the time period. Please do multiple revisions and drafts, unless you are completely satisfied do not submit the application.
  • Presentation of evidence in the right format and capturing it rightly is important too, I found this Chrome plugin named “Fireshot” very handy as you can convert web-pages to PDF/Images on the fly, capture entire page or selected area etc.
  • While preparing documentation and arranging evidence keep yourself in the Tech Nation shoes and think from their point of view, you need to show what extra have you done, what makes you different from the crowd and how can you be an asset to the UK.
Disclaimer: I hope this information suits your purpose. Any application you wish to make further to Tech nation/HomeOffice is entirely on your own risk.

The post How I got Tier-1 Exceptional Talent Visa and how can you get it too. appeared first on Little Big Extra.

]]>
http://littlebigextra.com/how-i-got-tier-1-exceptional-talent-visa/feed/ 8
Five points guide to become a Docker Guru for beginners http://littlebigextra.com/five-points-guide-to-become-a-docker-guru/ http://littlebigextra.com/five-points-guide-to-become-a-docker-guru/#respond Tue, 25 Sep 2018 15:50:48 +0000 http://www.littlebigextra.com/?p=1328 Introduction When it comes to Docker many people ask about the courses for learning and mastering containerization and deployment using Docker. Based on my experience of using Docker I feel the best resources to learn Docker are available at Docker Documentation. Here I will list out 5 basic steps which will help all, who wish to […]

The post Five points guide to become a Docker Guru for beginners appeared first on Little Big Extra.

]]>
Introduction

When it comes to Docker many people ask about the courses for learning and mastering containerization and deployment using Docker. Based on my experience of using Docker I feel the best resources to learn Docker are available at . Here I will list out 5 basic steps which will help all, who wish to learn and master Docker 

 

5 Points Guide to become a Docker Guru

1. Installing Docker on your Operating system/local machine
 

The first baby step starts with installing Docker on your local machine. Make sure the docker recommendations for memory (at least 4GB of RAM ) are hardware requirements are met.   Docker is available for both  and .

Docker Community Edition is open source version which can be used free of cost and has most of the docker features minus support. 

When the installation finishes, Docker starts automatically, and you should see a Whale Icon in the notification area. which means Docker is running, and accessible from a terminal.  Open a command-line terminal and Run docker version to check the version.  Run docker run hello-world to verify that Docker can pull and run images from DockerHub. You may be asked for your DockerHub credentials for pulling Images. 

2. Familiarizing yourself with docker pull and docker run commands 

Next step would be to familiarize yourself with docker pull and docker run commands.  docker pull gets the images from docker registry and docker run runs the downloaded image. The running image is also called Docker Container.

If you just use docker run command, then it will first pull the image and then start running the image.  While using docker run make sure that you familiarize yourself, especially with the various flag which can be used. There are many flags which can be used but some of the handy ones are as listed below 

  • -d: detached or foreground running 
  •  –name: If you do not assign a container name with the –name option, then the daemon generates a random string name for you. 
  • -P: Publish exposed ports to the host interfaces   

At this point, I will strongly advise downloading . Kitematic’s one-click install gets Docker running on your Mac and lets you control your app containers from a graphical user interface (GUI). If you missed any flags with your run command, you can fix them using Kitematic’s UI. Also, another feature which I like about kitematic is that it is easy to remove unused images, get into the bash shell of Docker container, see the logs etc.   

3. Creating Images using Dockerfile and pushing images to the registry using docker push  

Docker file is the building block of Docker images and containers. Dockerfiles use a simple DSL which allows you to automate the steps to create an image. A Docker image consists of read-only layers each of which represents a Dockerfile instruction. The layers are stacked and each one is a delta of the changes from the previous layer.  

Once your application is ready and now you want to package it into a Docker Image you will need to use Docker file DSL instructions. If all is well, at this point and all instructions of DockerFile has been completed and a docker image has been created then do a sanity check using docker run command to verify if things are working. 

Use docker push to share your images to the  registry or to a self-hosted one so they can be used across various environments and by various users/team members.   

4. Docker Compose and Persistent Data Storage 

Considering that now you are master with the all the above commands and able to create docker images, the next would be to use multiple docker images. With Compose, you use a YAML file to . You can configure as many containers as you want, how they should be built and connected, and where data should be stored. When the YAML file is complete, you can run a single command to build, run, and configure all of the containers. 

Since you have now multiple Docker containers running and interacting with each other, now you want to persist the data too. Docker Volumes are your saviour for this cause, you can use the –v or –mount flag to map data from Docker Containers to disks. Since Docker Containers are ephermal, once they are killed any data stored within them will also be lost, hence it is important to use the docker volumes so data can be persisted.   

5. Docker Swarm 

So now you have multiple containers running using docker-compose, the next step is to ensure availability and high performance for your application by distributing it over the number of Docker hosts inside a cluster.   With Docker Swarm, you can control a lot of things,  

  • You can have multiple instances of a single service(container) running on same/different machines. 
  • You can scale up or scale down the number of containers at the runtime. 
  • Service discovery, rolling updates and load balancing are provided by default. 

If you are using Docker Swarm make sure that you familiarize yourself with Quorum of Nodes and Master/Worker configuration. The managers in Docker Swarm need to define a quorum of managers which, in simple terms, means that the number of available manager nodes should be always greater or equal to (n+1)/2, where n is the number of manager nodes. So if you have 3 manager nodes, 2 should be always up, and if you have 5 manager nodes, 3 should be up. Also, it is a good idea to have managers running in different geographic locations.

Hopefully, the above points will help in giving insight into Docker world for novices. I have documented my docker journey over here Docker Archives – Little Big Extra.

The best way to learn is by doing it.   

The post Five points guide to become a Docker Guru for beginners appeared first on Little Big Extra.

]]>
http://littlebigextra.com/five-points-guide-to-become-a-docker-guru/feed/ 0
Configuring Kibana and ElasticSearch for Log Analysis with Fluentd on Docker Swarm http://littlebigextra.com/using-kibana-and-elasticsearch-for-log-analysis-with-fluentd-on-docker-swarm/ http://littlebigextra.com/using-kibana-and-elasticsearch-for-log-analysis-with-fluentd-on-docker-swarm/#respond Sat, 01 Sep 2018 15:33:28 +0000 http://littlebigextra.com/?p=1055 Using Kibana and ElasticSearch for Log Analysis with Fluentd on Docker Swarm Introduction In my previous post, I talked about how to configure fluentd for logging for multiple Docker containers. The post explained how to create a single file for each micro service irrespective of its multiple instances it could have. However, Log files have […]

The post Configuring Kibana and ElasticSearch for Log Analysis with Fluentd on Docker Swarm appeared first on Little Big Extra.

]]>
Using Kibana and ElasticSearch for Log Analysis with Fluentd on Docker Swarm

Introduction

In my previous post, I talked about how to configure fluentd for logging for multiple Docker containers. The post explained how to create a single file for each micro service irrespective of its multiple instances it could have.
However, Log files have limitations it is not easy to extract analysis or find any trends.

Elastic Search and Splunk have become very popular in recent years as they give you allow you to events in real-time, visualise trends and search through logs.

Elastic Search and Kibana

Elastic Search is an open source search engine based on Apache Lucene.It is an extremely fast search engine and is commonly used for log analytics, full-text search and much more.
Along with Kibana,  which is a visualisation tool, Elasticsearch can be used for real-time analytics. With Kibana you can create intuitive charts and reports, filters, aggregations and trends based on data.

Changing the fluent.conf

Since this post is continuation of previous post, I will show you how to modify the fluent.conf for elastic search changes

All we need to do is that we need to add another “store” block like below

<store>
    @type elasticsearch
    host elasticsearch
    port 9200
    logstash_format true
    logstash_prefix logstash
    logstash_dateformat %Y%m%d
    include_tag_key true
    tag_key @log_name
    flush_interval 1s
  </store>

In the above config, we are telling that elastic search is running on port 9200 and the host is elasticsearch (which is docker container name). Also we have defined the general Date format and flush_interval has been set to 1s which tells fluentd to send records to elasticsearch after every 1sec.

This is how the complete configuration will look like

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>
<match tutum>
  @type copy
   <store>
    @type file
    path /fluentd/log/tutum.*.log
    time_slice_format %Y%m%d
    time_slice_wait 10m
    time_format %Y%m%dT%H%M%S%z
    compress gzip
    utc
    format json
  </store>
  <store>
    @type elasticsearch
    host elasticsearch
    port 9200
    logstash_format true
    logstash_prefix logstash
    logstash_dateformat %Y%m%d
    include_tag_key true
    tag_key @log_name
    flush_interval 1s
  </store>
</match>
<match visualizer>
  @type copy
   <store>
    @type file
    path /fluentd/log/visualizer.*.log
    time_slice_format %Y%m%d
    time_slice_wait 10m
    time_format %Y%m%dT%H%M%S%z
    compress gzip
    utc
    format json
  </store>
    <store>
    @type elasticsearch
    host elasticsearch
    port 9200
    logstash_format true
    logstash_prefix logstash
    logstash_dateformat %Y%m%d
    include_tag_key true
    tag_key @log_name
    flush_interval 1s
  </store>
</match>

Create Dockerfile with our custom configuration

So the next step is to create a custom image of fluentd which has the above configuration file.
Save above file as fluent.conf in a folder named conf and then create a file called DockerFile at the same level as conf folder

# fluentd/Dockerfile
FROM fluent/fluentd:v0.12-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--no-rdoc", "--no-ri", "--version", "1.9.2"]
RUN rm /fluentd/etc/fluent.conf
COPY ./conf/fluent.conf /fluentd/etc

In this Docker file as you can see we are replacing the fluent.conf in the base image with the version of ours and also installing elastic search plugin

Now let us create a Docker image by

run "docker build -t ##YourREPOname##/myfluentd:latest ."

and then push it to the docker hub repository
"docker push ##YourREPOname##/myfluentd"


Elastic Search and Kibana Docker Image

This is how the YAML configuration for ElasticSearch looks like

elasticsearch:
    image: elasticsearch
    ports:
      - "9200:9200"
    networks:
      - net
    environment:
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    logging:
        driver: "json-file"
        options:
          max-size: 10M
          max-file: 1  
    deploy:
      restart_policy:
        condition: on-failure
        delay: 20s
        max_attempts: 3
        window: 120s
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      update_config:
        delay: 2s
      resources:
        limits:
          memory: 1000M
    volumes:
      - ./esdata:/usr/share/elasticsearch/data

Things to note here for this elastic search configuration file

  • The name of the container is set to elasticsearch, this is same to whta has been mentioned in fluent.conf
  • Port 9200 has been exposed to access elastic search locally too
  • The environment variables are important here as they restrict the max heap space this container can have and disable any Elasticsearch memory from being swapped out.
  • Using Logging Driver as JSON File as I want to restrict the log file size
  • Storing the elastic search data on a directory called “esdata” so data can persist between container restarts

This is how the YAML configuration for Kibana looks like

kibana:
    image: kibana
    ports:
      - "5601:5601"
    networks:
      - net
    logging:
        driver: "json-file"
        options:
           max-size: 10M
           max-file: 1        
    deploy:
      restart_policy:
        condition: on-failure
        delay: 20s
        max_attempts: 3
        window: 120s
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      update_config:
        delay: 2s

Things to note here for this kibana configuration file

  • The port 5601 has been exposed locally to access Kibana dashboard

Complete Config file

So now we need a complete docker-compose file which will have whoami service with multiple instances, docker visualiser service along with elastic, kibana and fluentd services.
This is how the complete files looks like

version: "3"
 
services:
       
  whoami:
    image: tutum/hello-world
    networks:
      - net
    ports:
      - "80:80"
    logging:
      driver: "fluentd"# Logging Driver
      options:
        tag: tutum    # TAG 
    deploy:
      restart_policy:
           condition: on-failure
           delay: 20s
           max_attempts: 3
           window: 120s
      mode: replicated
      replicas: 4
      placement:
        constraints: [node.role == worker]
      update_config:
        delay: 2s
 
  vizualizer:
      image: dockersamples/visualizer
      volumes:
         - /var/run/docker.sock:/var/run/docker.sock
      ports:
        - "8080:8080"
      networks:
        - net
      logging:
        driver: "fluentd"
        options:
         tag: visualizer   #TAG 
      deploy:
          restart_policy:
             condition: on-failure
             delay: 20s
             max_attempts: 3
             window: 120s
          mode: replicated # one container per manager node
          replicas: 1
          update_config:
            delay: 2s
          placement:
             constraints: [node.role == manager]
 
        
  fluentd:
    image: abhishekgaloda/myfluentd
    volumes:
      - ./Logs:/fluentd/log
    ports:
      - "24224:24224"
      - "24224:24224/udp"
    networks:
      - net
    deploy:
      restart_policy:
           condition: on-failure
           delay: 20s
           max_attempts: 3
           window: 120s
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      update_config:
        delay: 2s
 
  elasticsearch:
    image: elasticsearch
    ports:
      - "9200:9200"
    networks:
      - net
    environment:
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    logging:
        driver: "json-file"
        options:
          max-size: 10M
          max-file: 1  
    deploy:
      restart_policy:
        condition: on-failure
        delay: 20s
        max_attempts: 3
        window: 120s
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      update_config:
        delay: 2s
      resources:
        limits:
          memory: 1000M
    volumes:
      - ./esdata:/usr/share/elasticsearch/data    
      
  kibana:
    image: kibana
    ports:
      - "5601:5601"
    networks:
      - net
    logging:
        driver: "json-file"
        options:
           max-size: 10M
           max-file: 1        
    deploy:
      restart_policy:
        condition: on-failure
        delay: 20s
        max_attempts: 3
        window: 120s
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      update_config:
        delay: 2s  
 
networks:
  net:

You can run above services on docker swarm by using below command, make sure you save the file by the name docker-swarm.yml

docker stack deploy -c docker-swarm.yml test

Accessing kibana and elasticsearch

Once you make sure that all services are up and running using

docker service ls

you can access Kibana on
http://##domain#Or#IP##:5601/
Once You see something similar like below click on Create Button and then Discover on top left, you should see some bars indicating logs

Kibana Dashboard on Docker Swarm
Now if you click on http://##domain#Or#IP##/hello the whomai container will generate some logs which should appear on Kibana provided the right time has been chosen and auto-refresh has been enabled. See the screenshot below.

Kibana offers a lot of ways to create the visualization like charts, graphs etc for log analysis which can be explored further.

Logs in Elastic search + Kibana dashboard

Drop in suggestions or comments for any feedback.

Follow this Video for demonstration

The post Configuring Kibana and ElasticSearch for Log Analysis with Fluentd on Docker Swarm appeared first on Little Big Extra.

]]>
http://littlebigextra.com/using-kibana-and-elasticsearch-for-log-analysis-with-fluentd-on-docker-swarm/feed/ 0
How to add Subject Alt Names or multiple domains in a key-store and self signed certificate http://littlebigextra.com/how-to-add-subject-alt-names-or-multiple-domains-in-a-key-store-and-self-signed-certificate/ http://littlebigextra.com/how-to-add-subject-alt-names-or-multiple-domains-in-a-key-store-and-self-signed-certificate/#respond Wed, 22 Aug 2018 16:34:11 +0000 http://www.littlebigextra.com/?p=1299 How to add multiple domains(subject alt names) into certificate and a keystore(.jks) file Introduction In this article, we will see how to add multiple domains also known as the Subject alt name in the JKS file. If you are getting SSL handshake exception and your application is complaining about [crayon-61fa6b83baa34618588390/] then basically it means that […]

The post How to add Subject Alt Names or multiple domains in a key-store and self signed certificate appeared first on Little Big Extra.

]]>
How to add multiple domains(subject alt names) into certificate and a keystore(.jks) file

Introduction

In this article, we will see how to add multiple domains also known as the Subject alt name in the JKS file. If you are getting SSL handshake exception and your application is complaining about

****javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

then basically it means that your JKS file is missing the required domain on which you are trying to access the application.

Using Open SSL and the key tool to add multiple domains

    1. Copy the openssl.cnf into a current directory
      #For MAC use below command
      cp /etc/ssl/openssl.cnf .
      
      #For RHELinux(Centos7) 
      cp /etc/pki/tls/openssl.cnf .
      
      #For Windows you will need to install openssl and then find out where the corresponding file is and use the below defined Method -2
    2. Now append the ‘[ subject_alt_name ]’ to end of openssl.cnf file
       echo '[ subject_alt_name ]' >> openssl.cnf
    3. Now add multiple domain names, in the below example I am adding multiple domains, along with localhost
       echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf
    4. Create the .public  and private key, also note here we are adding the CN name and organisation details. The advantage of adding it in particular command is that you won’t be prompted with any details
      openssl req -x509 -nodes -newkey rsa:2048 -config openssl.cnf -extensions subject_alt_name -keyout private.key -out self-signed.pem -subj '/C=gb/ST=edinburgh/L=edinburgh/O=mygroup/OU=servicing/CN=www.example.com/emailAddress=postmaster@example.com' -days 365

      The above command should generate a set of public and private keys. The private key will be generated in a file called private.key and the public key or certificate will be generated in a file called self-signed.pem.Also please note that above command also defines the country, state, location, organization name for simplification only XX has been added and the validity for above certificate is for a year which is controlled by ‘-days 365’. Feel free to change as per your needs.
    5. Verify the .pem file has been generated successfully
      openssl x509 -in self-signed.pem -text -noout

      From the above command, you should see the multiple domain names which have been added. This verifies that our
      Certificate:
                  Subject: C=gb, ST=edinburgh, L=edinburgh, O=mygroup, OU=servicing, CN=www.example.com/emailAddress=postmaster@example.com
              Subject Public Key Info:
                  Public Key Algorithm: rsaEncryption
                      Public-Key: (2048 bit)
                      Modulus:
                     45:17:ea:d5:87:
                          30:17:e1:50:4a:c7:67:9b:5f:35:c3:0b:0e:f2:83:
                          32:19.......
                      Exponent: 65537 (0x10001)
              X509v3 extensions:
                  X509v3 Subject Alternative Name: 
                      DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS:localhost
          Signature Algorithm: sha256WithRSAEncryption
               8c:7d:85:5e:37:d2:e7:09:f5:3e:ce:73:d4:d5:3e:5a:ee:e2:
    6. Export the public key (.pem) file to PKS12 format. This will prompt you for password
      openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in self-signed.pem -inkey private.key -name myalias -out keystore.p12

    7. Create a.JKS from self-signed PEM (Keystore)
      keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12

      The above-generated.jks file can you use within your Java application.

    8. Verify is the JKS has been correctly created
      keytool -list -v -keystore keystore.jks

      This should show you the multiple subject alt names added
      Signature algorithm name: SHA256withRSA
      Subject Public Key Algorithm: 2048-bit RSA key
      Version: 3
      
      Extensions: 
      
      SubjectAlternativeName [
        DNSName: example.mydomain1.com
        DNSName: example.mydomain2.com
        DNSName: example.mydomain3.com
        DNSName: localhost
      ]

       
    9. Generate a Certificate from above Keystore or JKS file
      keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt

       
    10.  Since the above certificate is Self Signed and is not validated by CA, it needs to be added in Truststore(Cacerts file in below location)
      sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts

       

      Tip – Keystore Explorer is a nice tool to verify the trust store, examine certificates etc

The post How to add Subject Alt Names or multiple domains in a key-store and self signed certificate appeared first on Little Big Extra.

]]>
http://littlebigextra.com/how-to-add-subject-alt-names-or-multiple-domains-in-a-key-store-and-self-signed-certificate/feed/ 0
How to fix javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present http://littlebigextra.com/how-to-fix-javax-net-ssl-sslhandshakeexception-java-security-cert-certificateexception-no-subject-alternative-names-present/ http://littlebigextra.com/how-to-fix-javax-net-ssl-sslhandshakeexception-java-security-cert-certificateexception-no-subject-alternative-names-present/#comments Tue, 21 Aug 2018 11:09:16 +0000 http://www.littlebigextra.com/?p=1288 How to fix javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present Introduction In this article, we will focus on how to resolve the SSLHandshakeException and possible cause behind it. If you are getting below error, let’s find out how to resolve it. [crayon-61fa6b83bac46074728097-i/] Cause of error The reason, we get above error is that CN(Common name) […]

The post How to fix javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present appeared first on Little Big Extra.

]]>
How to fix javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

Introduction

In this article, we will focus on how to resolve the SSLHandshakeException and possible cause behind it. If you are getting below error, let’s find out how to resolve it.

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1959)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:328)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:322)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1614)

Cause of error

SSL Handshake Exception

The reason, we get above error is that CN(Common name) defined in your certificate is not matching with the domain the application is running on.
For e.g, In your certificate, the CN name is defined as www.example.com but you may be running the application say a URL which is like http://localhost:8080/api

How to fix the above error

There are 2 easy ways to fix the above error

Use this Command to check what is the CN name defined in your certificate

keytool -printcert -v -file certifcate.crt 
#Where certificate.crt is the name of your certificate

You should get some response like this

Owner: EMAILADDRESS=postmaster@example.com, CN=www.mydomain.com, OU=organisation, O=my group, L=edinburgh, ST=edinburgh, C=gb
Issuer: EMAILADDRESS=postmaster@example.com, CN=www.mydomain.comm, OU=organisation, O= my group, L=edinburgh, ST=edinburgh, C=gb
Serial number: dcc3d4ffe7a016f2
Valid from: Tue Jun 26 12:41:05 BST 2018 until: Wed Jun 26 12:41:05 BST 2019
Certificate fingerprints:
	 MD5:  32:FE:3A:35:D6:7F:C0:4A:0D:95:99:10:9A:71:1D:DC
	 SHA1: 10:FF:59:F0:72:83:40:B8:5D:6C:D1:64:33:90:22:17:2B:0E:37:A0
	 SHA256: C3:1D:34:BA:9D:C4:00:66:9E:C8:91:29:1B:0B:96:5F:D2:00:17:95:DB:72:6E:2C:7B:1E:9B:20:5E:08:1F:60
Signature algorithm name: SHA256withRSA

Pay close attention to the first line in above output CN=www.mydomain.com, if you are running the application from the above domain, you should not encounter the above error.

So to fix the above error simply use one of the below approaches

  • Run the application on the same ‘CN’, as defined in your certificates.

OR

  • Along with CN name you can add Subject alt names in your certificate, which is like adding more than one domain in the certificate. Link below describes the process of adding multiple domains(subject-alt-name) to jks file and also to a certificate.

The post How to fix javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present appeared first on Little Big Extra.

]]>
http://littlebigextra.com/how-to-fix-javax-net-ssl-sslhandshakeexception-java-security-cert-certificateexception-no-subject-alternative-names-present/feed/ 1
How to fix PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException http://littlebigextra.com/how-to-fix-pkix-path-building-failed-sun-security-provider-certpath-suncertpathbuilderexception/ http://littlebigextra.com/how-to-fix-pkix-path-building-failed-sun-security-provider-certpath-suncertpathbuilderexception/#comments Fri, 17 Aug 2018 14:36:33 +0000 http://www.littlebigextra.com/?p=1271 How to fix PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException Introduction In the last article, we were trying to enable communication over https between 2 applications using the self-signed certificate. Once the certificate was added and when we ran the application some users would have got the below error. [crayon-61fa6b83bae64489396848/] In this article, we will focus on […]

The post How to fix PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException appeared first on Little Big Extra.

]]>
How to fix PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException

Introduction


In the last article, we were trying to enable communication over https between 2 applications using the self-signed certificate. Once the certificate was added and when we ran the application some users would have got the below error.

org.springframework.web.client.ResourceAccessException: 
I/O error on GET request for "https://localhost:8100/customer/": sun.security.validator.ValidatorException: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target; 
nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

In this article, we will focus on how to fix the above error.

Cause of error

The reason, we get above error is that JDK is bundled with a lot of trusted Certificate Authority(CA) certificates into a file called ‘cacerts’ but this file has no clue of our self-signed certificate. In other words, the cacerts file doesn’t have our self-signed certificate imported and thus doesn’t treat it as a trusted entity and hence it gives the above error.

How to fix the above error

To fix the above error, all we need is to import the self-signed certificate into the cacerts file.

  • First, locate the cacerts file. We will need to find out the JDK location. If you are running your application through one of the IDE’s like Eclipse or IntelliJ Idea go to project settings and figure out what is the JDK location.
    For e.g on a Mac OS typical location of cacerts file would be at this location
    /Library/Java/JavaVirtualMachines/
    {{JDK_version}}/Contents/Home/jre/lib/security

    on a Window’s machine it would be under
    {{Installation_directory}}/{{JDK_version}}/jre/lib/security
  • Once you have located the cacerts file, now we need to import our self-signed certificate to this cacerts file. Check the last article, if you don’t know how to generate the self-signed certificate correctly.
  • If you don’t have a certificate file(.crt) and just have a .jks file you can generate a .crt file by using below command. In case you already have a .crt/.pem file then you can ignore below command

 ##To generate certificate from keystore(.jks file) ####
keytool -export -keystore keystore.jks -alias selfsigned -file selfsigned.crt

Above step will generate a file called selfsigned.crt

  • Import the certificate to cacerts

#### Now add the certificate to JRE/lib/security/cacerts (trustore) #####
keytool -importcert -file selfsigned.crt -alias selfsigned -keystore {{cacerts path}}

for e.g

keytool -importcert -file selfsigned.nextgen.crt -alias selfsigned.nextgen -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts

 

That’s all, restart your application and it should work fine. If it still doesn’t work and get an SSL handshake exception. It probably means you are using different domain then registered in the certificate.

The post How to fix PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException appeared first on Little Big Extra.

]]>
http://littlebigextra.com/how-to-fix-pkix-path-building-failed-sun-security-provider-certpath-suncertpathbuilderexception/feed/ 1
How to enable communication over https between 2 spring boot applications using self signed certificate http://littlebigextra.com/how-to-enable-communication-over-https-between-2-spring-boot-applications-using-self-signed-certificate/ http://littlebigextra.com/how-to-enable-communication-over-https-between-2-spring-boot-applications-using-self-signed-certificate/#respond Wed, 08 Aug 2018 15:00:54 +0000 http://www.littlebigextra.com/?p=1263 How to enable communication over https between 2 spring boot applications using self signed certificate Introduction In this tutorial, we will try to cover how we can enable HTTPS communication over 2 Spring boot applications. HTTPS was developed for exchanging confidential information in a secured manner by making use of encryption using public and private keys in […]

The post How to enable communication over https between 2 spring boot applications using self signed certificate appeared first on Little Big Extra.

]]>
How to enable communication over https between 2 spring boot applications using self signed certificate

Introduction


In this tutorial, we will try to cover how we can enable HTTPS communication over 2 Spring boot applications. HTTPS was developed for exchanging confidential information in a secured manner by making use of encryption using public and private keys in order to prevent unauthorized access.

In the production environment, you will need to install a  certificate issued by a certificate authority (CA) which will validate identity and then and issue certificates. CA certificates contain a public key corresponding to a private key. The CA owns the private key and uses it to sign the certificates it issues. Certificates help prevent the use of fake public keys for impersonation.

However, CA-signed certificates might not be available in the lower environments like DEV or for local testing, in this case, you might want to establish that your API’s are able to talk over HTTPS and this is where you can make use of the self-signed certificate. 

Generating a self-signed certificate

We will use a self-signed certificate, to generate one we will need OpenSSL installed on our machine. In MacOs and Linux machines it is preinstalled, however, in windows you have to install it. Keytool can also be used to generate the self-signed certificates

    • You can use below command to generate a self-signed certificate and private key. Also, note that the CN (the Common name is localhost) as we will be running the application on localhost. If you want to run on a domain name you can either replace the CN name with Domain name or use Subject Alt Names to use multiple domains. Read here —> how to

 

  • Generate Public and Private keys
    openssl req -x509 -nodes -newkey rsa:2048 -keyout private.key -out selfsigned.pem -subj '/C=gb/ST=XX/L=XX/O=XX/OU=XX/CN=localhost/emailAddress=postmaster@example.com' -days 365

    The above command should generate set of public and private keys. The private key will be generated in a file called private.key and the public key or certificate will be generated in a file called selfsigned.pem.

    Also please note that above command also defines the country, state, location, organization name for simplification only XX has been added and the validity for above certificate is for a year which is controlled by ‘-days 365’. Feel free to change as per your needs.

  • Check if the generated PEM file is correct

    openssl x509 -in selfsigned.pem -text -noout

    If the certificate has been generated successfully, then basically above command should generate something like this, basically listing out the expiry date, the signature algorithm etc.

    Certificate:
        Data:
            Version: 1 (0x0)
            Serial Number: 10095699467019317110 (0x8c1b212d0ac96f76)
        Signature Algorithm: sha256WithRSAEncryption
            Issuer: C=gb, ST=XX, L=XX, O=XX, OU=XX, CN=loclhost/emailAddress=postmaster@example.com
            Validity
                Not Before: Jun 27 10:52:32 2018 GMT
                Not After : Jun 27 10:52:32 2019 GMT
            Subject: C=gb, ST=XX, L=XX, O=XX, OU=XX, CN=loclhost/emailAddress=postmaster@example.com
            Subject Public Key Info:

     

  • Combine your key and certificate in a PKCS#12 (P12) bundle: The PKCS#12 or PFX format is a binary format for storing the server certificate, any intermediate certificates, and the private key into a single encryptable file. The below command will generate a file called keystore.12

    openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in selfsigned.pem -inkey private.key -name selfsigned -out keystore.p12

     

  • Create a JKS from self-signed PEM: To implement X.509 authentication in a Spring application, we’ll first create a keystore in the Java Key-Store (JKS) format.

    keytool -importkeystore -srcstoretype PKCS12 -srckeystore keystore.p12 -destkeystore keystore.jks -deststoretype pkcs12

     

Adding the Keystore.jks in the Spring boot application

Add the following configuration to your spring boot application.yaml file and copy the above-generated keystore.jks file in src/main/resources folder

server:
  ssl:
    key-store-type: PKCS12
    key-store: 'classpath:keystore.jks'
    key-store-password: changeit
    key-alias: selfsigned

 

Now restart your application and see if you can see something in the logs ” Tomcat started on port(s): 8080 (https) with context path.. ”

This confirms that application will work only on https and not on http.

Calling from one Spring Boot application to another

Considering the above changes went well and both the Spring boot application has started and run on different ports with HTTPS enabled (server.port: port_number property can be used to configure different port numbers)

To call the other spring Boot application all we need is change the URL to use ‘https’

You can use RestTemplate to make a call

@Autowired
RestTemplate restTemplate;

void someRandomFunction(){
restTemplate.getForObject("https://localhost:8090/SpringBootApp1", Response.class)
}

 

If you get the below exception

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8100/customer/session/1b63e4b8-a91b-4742-b70a-8f113271212": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

If you get SSL Handshake exception

****javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)

The post How to enable communication over https between 2 spring boot applications using self signed certificate appeared first on Little Big Extra.

]]>
http://littlebigextra.com/how-to-enable-communication-over-https-between-2-spring-boot-applications-using-self-signed-certificate/feed/ 0
How to maintain Session Persistence (Sticky Session) in Docker Swarm http://littlebigextra.com/how-to-maintain-session-persistence-sticky-session-in-docker-swarm-with-multiple-containers/ http://littlebigextra.com/how-to-maintain-session-persistence-sticky-session-in-docker-swarm-with-multiple-containers/#comments Sun, 06 May 2018 15:19:01 +0000 http://littlebigextra.com/?p=1016 How to maintain Session Persistence(Sticky Session) in Docker Swarm with multiple containers Introduction Stateless services are in vogue and rightfully so as they are easy to scale up and are loosely coupled. However, it is practically impossible to stay away from stateful services completely. For example, say you might need a login application where user session […]

The post How to maintain Session Persistence (Sticky Session) in Docker Swarm appeared first on Little Big Extra.

]]>
How to maintain Session Persistence(Sticky Session) in Docker Swarm with multiple containers

Introduction

Stateless services are in vogue and rightfully so as they are easy to scale up and are loosely coupled. However, it is practically impossible to stay away from stateful services completely. For example, say you might need a login application where user session details need to be maintained across several pages.

Session state can be maintained either using

  • Session Replication
  • Session Stickiness

or a combination of both.

 

Maintaining a user session is relatively easy if you are using a typical monolithic architecture where your application is installed on a couple of servers and you can change the configuration in servers to facilitate session replication using some cache mechanism or session stickiness using a load balancer/reverse proxy.

However, In the case of Microservices, where the scale can be as large from 10 to 10000’s instances the session replication might slow up things as each and every service need to look up at the centralised cache to get session information.

The other approach Session Stickiness where each following request should keep going to the same server( Docker container)  and hence preserving the session will be looked at in this article.

Why session persistence is hard to maintain with containers

Load balancer typically works on Layer 7 OSI model, the application layer (HTTP protocol at this layer) and then distributes the data across multiple machines, but Docker ingress routing mesh works at level 4 in OSI layer.

Someone in StackOverflow has summarized the solution for above problem as- To implement sticky sessions, you would need to implement a reverse proxy inside of docker that supports sticky sessions and communicates directly to the containers by their container id (rather than doing a DNS lookup on the service name which would again go to the round robin load balancer). Implementing that load balancer would also require you to implement your own service discovery tool so that it knows which containers are available.

Possible options explored

Take -1

So I tried implementing the reverse proxy with Nginx and it worked with multiple containers on a single machine but when deployed on Docker Swarm it doesn’t work probably because I was using the service discovery by name and as suggested above, I should use containerId to communicate and not container names.

Take -2

Read about the Jwilder Nginx proxy which works for everyone and it worked on my local but when deployed on Swarm it won’t generate anything any container IP’s inside the

upstream{server}

Take -3

Desperate enough by this time I was going through all possible solutions people have to offer about on the internet (stack overflow, Docker community forums..) and one gentleman has mentioned something about Traefik. Eyes glittered when I read that it works on SWARM and here I go.

Sticky Session with Traefik in Docker Swarm with multiple containers

Even though  I was very comfortable with Nginx and assumed that learning will again be an overhead. It wasn’t the case Traefik is simple to learn and easy to understand and good thing is that you need not fiddle with any of the conf files.

The only constraint is that Traefik should run on manager node

I have tested the configuration with Docker compose version 3 which is the latest and deployed using Docker stack deploy

To start off you need to create a docker-compose.yml (version 3) and add the load balancer Traefik Image. This is how it looks like

loadbalancer:
    image: traefik
    command: --docker \
      --docker.swarmmode \
      --docker.watch \
      --web \
      --loglevel=DEBUG
    ports:
      - 80:80
      - 9090:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      restart_policy:
        condition: any
      mode: replicated
      replicas: 1
      update_config:
        delay: 2s
      placement:
         constraints: [node.role == manager]
    networks:
      - net

Few things to note here

  • Traefik listens to Docker daemon on manager node and keeps aware of new worker nodes, so there is no need to restart if you scale your services.
    volumes: – /var/run/docker.sock:/var/run/docker.sock
  • Traefik provides a dashboard to check the worker nodes health so port 9090 can be kept inside a firewall for monitoring purpose.
    Also, note that
    placement: constraints: [node.role == manager]
     specifies that traefik run only on manager node.

Adding the Image for sticky session

To add a Docker Image which will hold session stickyness we need to add something like this

whoami:
    image: tutum/hello-world
    networks:
      - net
    ports:
      - "80"
    deploy:
      restart_policy:
        condition: any
      mode: replicated
      replicas: 5
      placement:
        constraints: [node.role == worker]
      update_config:
        delay: 2s
      labels:
        - "traefik.docker.network=test_net"
        - "traefik.port=80"
        - "traefik.frontend.rule=PathPrefix:/hello;"
        - "traefik.backend.loadbalancer.sticky=true"

This is a hello world image which displays the container name its running on. We are defining in this file to have 5 replicas of this container. The important section where traefik does the magic is in “labels”

  • - "traefik.docker.network=test_net"
    Tells on which network this image will run on. Please note that the network name is test_net, where test is the stack name. In the load balancer service we just gave net as name.
  • - "traefik.port=80"
    This Helloworld is running on docker port 80 so lets map the traefik port to 80
  • - "traefik.frontend.rule=PathPrefix:/hello"
    All URLs starting with {domainname}/hello/ will be redirected to this container/application
  • - "traefik.backend.loadbalancer.sticky=true"
    The magic happens here, where we are telling to make sessions sticky.

The Complete Picture

Try to use the below file as it is and see if it works, if it does then fiddle with it and make your changes accordingly.

You will need to create a file called docker-compose.yml on your Docker manager node and run this command

docker stack deploy -c docker-compose.yml test
wher the “test” is the namespace.

Read Here about deploying in Swarm: How to Install Stack of services in Docker Swarm

version: "3"

services:

  whoami:
    image: tutum/hello-world
    networks:
      - net
    ports:
      - "80"
    deploy:
      restart_policy:
        condition: any
      mode: replicated
      replicas: 5
      placement:
        constraints: [node.role == worker]
      update_config:
        delay: 2s
      labels:
        - "traefik.docker.network=test_net"
        - "traefik.port=80"
        - "traefik.frontend.rule=PathPrefix:/hello;"
        - "traefik.backend.loadbalancer.sticky=true"

  loadbalancer:
    image: traefik
    command: --docker \
      --docker.swarmmode \
      --docker.watch \
      --web \
      --loglevel=DEBUG
    ports:
      - 80:80
      - 9090:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      restart_policy:
        condition: any
      mode: replicated
      replicas: 1
      update_config:
        delay: 2s
      placement:
         constraints: [node.role == manager]
    networks:
      - net

networks:
  net:

Now you can test this service by http://{Your-Domain-name}/hello and http://{Your-Domain-name}:9090 should show us a Traefik dashboard.

Though there are 5 replicas of above “whoami” service, it should always display the same container ID. If it does congratulations your session peristence is working.

This is how the dashboard of Traefik looks like

Testing session stickness in local machine

In case you don’t have a swarm node and just want to test it on your localhost machine. You can use the following docker-compose file. To run successfully create a directory called test( required for namespace, as we have given our network name as  test_net

- "traefik.docker.network=test_net"
, change the directory name if you have different network) and run
docker-compose up -d

version: "3"

services: 

  whoami:
    image: tutum/hello-world
    networks:
      - net
    ports:
      - "80"  
    labels:
        - "traefik.backend.loadbalancer.sticky=true"
        - "traefik.docker.network=test_net"
        - "traefik.port=80"
        - "traefik.frontend.rule=PathPrefix:/hello"
      
  
  loadbalancer:
    image: traefik
    command: --docker \
      --docker.watch \
      --web \
      --loglevel=DEBUG
    ports:
      - 80:80
      - 25581:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - net

networks:
  net:

Docker-Compose should create the required services and whoami service should be available on http://localhost/hello.

Scale say this service to 5

docker-compose scale whoami=5
and test

Follow this Video to see things in action

The post How to maintain Session Persistence (Sticky Session) in Docker Swarm appeared first on Little Big Extra.

]]>
http://littlebigextra.com/how-to-maintain-session-persistence-sticky-session-in-docker-swarm-with-multiple-containers/feed/ 22
Part-2: Authorising user using Spring Social (Google, Facebook, LinkedIn) and Spring Security http://littlebigextra.com/part-2-authorising-user-using-spring-social-google-facebook-linkedin-spring-security/ http://littlebigextra.com/part-2-authorising-user-using-spring-social-google-facebook-linkedin-spring-security/#comments Mon, 04 Dec 2023 12:20:52 +0000 http://littlebigextra.com/?p=1204 Part-2: Authorising user using Spring Social (Google, FaceBook and LinkedIn) and Spring Security   Introduction In the last part, I had demonstrated how we can use spring-social to authorize user using Facebook, Google and LinkedIn API’s. If you have not read the last part, I would request you to have a look and then come […]

The post Part-2: Authorising user using Spring Social (Google, Facebook, LinkedIn) and Spring Security appeared first on Little Big Extra.

]]>
Part-2: Authorising user using Spring Social (Google, FaceBook and LinkedIn) and Spring Security

 

Introduction

In the last part, I had demonstrated how we can use spring-social to authorize user using Facebook, Google and LinkedIn API’s. If you have not read the last part, I would request you to have a look and then come back to this part.

In this part, I am going to use spring security to allow only logged in users or authenticated users to navigate to secure pages, any user attempting to go to secure pages will be redirected to the Login page for authentication.
Once the user is authenticated, we will save his details to in-memory DB and then the user can log out and log in again.

Spring Security framework provides both authentication and authorization feature for applications. It also helps in preventing attacks like session fixation, clickjacking, cross-site request forgery, etc and good thing is that it can be customized easily to fit in different use cases.
In this tutorial, we will add spring security with spring social API to register users and then add the log on and log out functionality.

Step 1 – Adding Maven dependencies

The first step to start will be adding maven dependencies for Spring-security and also for thymeleaf-extras-spring security for using spring-security tags for displaying logged in user and roles etc on pages.
Add the following dependencies to the POM.xml

<dependency>
		        <groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
		</dependency>
		<dependency>
      	               <groupId>org.thymeleaf.extras</groupId>
                       <artifactId>thymeleaf-extras-springsecurity4</artifactId>
   	      </dependency>

Step 2 – Registering user on site

When logging on to social networking platforms, we didn’t need any form as we fetch the user details through the API. However, to register a user we will need a simple form to capture their details. We will create a view called registration.html under src/main/resources/templates/registration.html.
This form will have server-side validation and minimal CSS/JS


Source code of Registration page is as below

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Login</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width" />
<base href="/" />
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/webjars/font-awesome/css/font-awesome.min.css"></link>
</head>

<body>
	<div class="container" style="width:80%">
	<h1>Registration Page</h1>
	<br />
		<form action="#" th:action="@{/registration}" th:object="${userBean}" method="post" >
			<div class="form-group" >
				<label for="email" class="control-label col-sm-2">Email*</label>:: <input type="text" th:field="*{email}"  placeholder="Enter email"/>
				<div style="width:33%"  th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="alert alert-danger">Email Error</div>
			</div>
			<div class="form-group">
				<label for="firstName" class="control-label col-sm-2">First Name*</label>:: <input type="text" th:field="*{firstName}" />
				<div style="width:33%" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}" class="alert alert-danger">FirstName Error</div>
			</div>
			<div class="form-group">
				<label for="lastName" class="control-label col-sm-2">Last Name*</label>:: <input type="text" th:field="*{lastName}" />
				<div style="width:33%" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}" class="alert alert-danger">LastName Error</div>
			</div>
			<div class="form-group">
				<label for="password" class="control-label col-sm-2">Password*</label>:: <input type="text" th:field="*{password}" />
				<div style="width:33%" th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="alert alert-danger">Password Error</div>
			</div>
			<div class="form-group">
				<label for="passwordConfirm" class="control-label col-sm-2">Confirm Password*</label>:: <input type="text" th:field="*{passwordConfirm}" />
				<div style="width:33%" th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}" class="alert alert-danger">Password Error</div>
			</div>
			<div class="form-group">
				<label for="title" class="control-label col-sm-2">Title</label>:: <select th:field="*{title}">
					<option value="Mr" th:text="Mr"></option>
					<option value="Mrs" th:text="Mrs"></option>
				</select>
				<div th:if="${#fields.hasErrors('title')}" th:errors="*{title}">Title Error</div>
			</div>
			<div class="form-group">
				<label for="country" class="control-label col-sm-2">Country</label>:: <select th:field="*{country}">
					<option value="India" th:text="India"></option>
					<option value="UK" th:text="UK"></option>
					<option value="US" th:text="US"></option>
					<option value="Japan" th:text="Japan"></option>
				</select>
				<div th:if="${#fields.hasErrors('country')}" th:errors="*{country}" class="alert alert-danger">Country Error</div>
			</div>
			<input type="hidden" name="provider"  value="registration" />
			<div class="form-group">
				<button type="submit" class="btn btn-primary">Register</button>
			</div>
		</form>

	</div>
</body>
</html>

Modifying the Controller

To serve this Page, we will modify the existing Login controller

@GetMapping("/registration")
    public String showRegistration(UserBean userBean) {
	return "registration";
    }

Modifying the Login Page

Now let’s modify the login page and add the registration link on the home page, also we will add username and password fields so once the user is registered they can also log in.

This HTML fragment will add a username/password field along with an error message, in case of the user not found.

<form th:action="@{/login}" method="post" style="display: inline">
			<label for="username">Username</label>: <input type="text" id="username" name="username" autofocus="autofocus" /> <br />
			<label for="password">Password</label>: <input type="password" id="password" name="password" /> <br />
			<p th:if="${loginError}" class="alert alert-danger">Wrong email or password combination</p>
			<button type="submit" class="btn btn-primary">
				<span class="fa fa-user"></span>Login
			</button>
		</form>

 

Also, let’s add another code fragment which is to display the Logged In user, the role assigned and a Logout button

<div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()">
			Logged in user: <b><span sec:authentication="name"></span></b> | Roles: <b><span sec:authentication="authorities"></span></b>
				<form action="#" th:action="@{/logout}" method="post">
			       <button type="submit"  class="btn btn-danger btn-sm"
			       <span class="glyphicon glyphicon-log-out"></span>
			        Log out</button>
				</form>
		</div>

 

We will also need to csrf token to our existing forms for Google, Facebook and LinkedIn

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

This is how the Complete login.html looks like.

Login Page

Source code of login.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Login</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width" />
<base href="/" />
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/webjars/font-awesome/css/font-awesome.min.css"></link>
</head>

<body>

	<div class="container">
		
		<div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()">
			Logged in user: <b><span sec:authentication="name"></span></b> | Roles: <b><span sec:authentication="authorities"></span></b>
				<form action="#" th:action="@{/logout}" method="post">
			       <button type="submit"  class="btn btn-danger btn-sm">
			       <span class="glyphicon glyphicon-log-out"></span>
			        Log out</button>
				</form>
		</div>
		<br/>
		
		<h1>Login Using</h1>

		<form th:action="@{/login}" method="post" style="display: inline">
			<label for="username">Email   </label> : <input type="text" id="username" name="username" autofocus="autofocus" placeholder="Enter email"/> <br />
			<label for="password">Password</label>: <input type="password" id="password" name="password" /> <br />
			<p th:if="${loginError}" class="alert alert-danger">Wrong email or password combination</p>
			<button type="submit" class="btn btn-primary">
				<span class="fa fa-user"></span>Login
			</button>
		</form>
		
		<form action="/connect/google" method="POST" style="display: inline">
			<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> 
			<input type="hidden" name="scope" value="profile email" />
			<button type="submit" class="btn btn-danger">
				Google <span class="fa fa-google-plus"></span>
			</button>
		</form>

		<form action="/connect/facebook" method="POST" style="display: inline">
			<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> 
			<input type="hidden" name="scope" value="public_profile,email" />
			<button type="submit" class="btn btn-primary">
				Facebook <span class="fa fa-facebook"></span>
			</button>
		</form>

		<form action="/connect/linkedin" method="POST" style="display: inline">
		<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> 
			<input type="hidden" name="scope"
				value="r_basicprofile,r_emailaddress" />
			<button type="submit" class="btn btn-primary">
				LinkedIn <span class="fa fa-linkedin"></span>
			</button>
		</form>
		<br />
		<h3>
			<p class="bg-important">
				<a href="/registration" th:href="@{/registration}">Create Account</a>
			</p>
		</h3>
	</div>
</body>
</html>

Step 3 – Saving User to Database

Once the user is authenticated, we will need to save their details in a database. So we will use the annotations like @Entity and @Table to create an in-memory database(HSQLDB) and tables if they don’t exist. The DB details can be easily configured in application.properties,also the DB type can also be changed easily.
We will use the hibernate validator annotations @NotNull,@Size to make sure that input fields are validated on the server side before they can be, you can add client-side javascript if that suits.

package com.login.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;

@Entity(name = "user")
@Table(name = "user")
public class UserBean implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	@NotNull(message = "Email  cannot be empty")
	@Email(message = "Email Format is not valid")
	@Size(min = 3, max = 30, message = "Email can not be empty")
	@Id
	private String email;
	
	@NotNull(message = "First Name cannot be empty")
	@Size(min = 3, max = 30, message = "First Name cannot be less than 3 characters")
	private String firstName;

	@NotNull(message = "Last Name cannot be empty")
	@Size(min = 3, max = 30, message = "Last Name cannot be less than 3 characters")
	private String lastName;

	private String title;
	private String country;
	private String password;
	@Transient
	private String passwordConfirm;	
	private String provider;
	private String image;

	......getter/setter methods here ...

}

Saving the Bean using JPA Repository

We will be using to retrieve user details and save them.
Now we will create a new interface called UserRepository in package com.login.repository this interface will extend the JPARespository<T, ID> where T is UserBean in our Case and ID is the email(primary key).
We will define the abstract method findByEmail by passing email which has been defined as a primary key in UserBean class. To get the UserBean for a particular method all we need to do is inject the UserRepository and call findByemail method()

package com.login.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.login.model.UserBean;

public interface UserRepository extends JpaRepository<UserBean, String> {

    	UserBean findByEmail(String email);

}

The key advantage of using Spring Data is that it makes virtually our code DAO implementation-free, only above interface will be able to save and retrieve user details.

Step 4 – Adding the Spring Security

This is the crux of this tutorial, we will use the spring security to define which URL’s can be accessed only by insecurely and which one can only be accessed by login user.

We will add a class called SecurityConfig to allow users to

  • Allow URLs which start with CSS/** and /connect** to be accessed by all users
  • Allow /secure/* URL to be accessed only by logged in User
  • If any secure/* is accessed by unauthenticated user, redirect him to login page
  • In case of unsuccessful authentication, redirect user to /login-error
  • Add logout functionality

package com.login.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

//@formatter:off
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests().antMatchers("/css/**", "/connect/**").permitAll()
				.antMatchers("/secure/**")
				.authenticated()
				.and()
				.formLogin()
				.loginPage("/login")
				.defaultSuccessUrl("/secure/user")
				.failureUrl("/login-error")
				.permitAll()
				.and()
				.logout()
				.permitAll();
	}
	
	@Bean
	public BCryptPasswordEncoder bCryptPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
	}

}

// @formatter:on

As you might have noticed that we have added a bean for BCryptPasswordEncoder, this bean will be used for encrypting the password as hash, which is one of the safest technique to store passwords and decrypting hash is very tough ( Maybe not as tough as mining a BitCoin 🙂 )

Also, there is another method configureglobal(AuthenticationManagerBuilder auth) which basically defines that we have defined our own custom implementation of UserDetailsService, we will talk about it later.

Move user.html to secure/user.html

Since now we want our user.html to be only presented for logged in users we will move user.html from src/main/resources/templates to src/main/resources/templates/secure/user.html
Also, we will add the thymleaf authentication tags to display logged in Username and role.
This is how the complete user.html looks like, make sure it is in src/main/resources/templates/secure/user.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Login</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width" />
<meta name="ctx" th:content="${#httpServletRequest.getContextPath()}" />
<base href="/" />
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/webjars/font-awesome/css/font-awesome.min.css"></link>
</head>
<body>

	<div class="container">
		<h1>Secure Page</h1>
				<div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()">
			Logged in user: <b><span sec:authentication="name"></span></b> | Roles: <b><span sec:authentication="authorities"></span></b>
				<form action="#" th:action="@{/logout}" method="post">
			       <button type="submit"  class="btn btn-danger btn-sm">
			       <span class="glyphicon glyphicon-log-out"></span>
			        Log out</button>
				</form>
		</div>

		<br/>
		<form th:object="${loggedInUser}" method="post">
			<div class="row">
				<label for="email">Email :</label>
				<span th:text="*{email}" />
			</div>
			<div class="row">
				<label for="firstName">Name:</label>
				 <span th:text="*{firstName}" /> <span th:text="*{lastName}" />
			</div>
			<div class="row">
				<label for="image">Image:</label>
				 <img th:attr="src=@{*{image}}" style="width: 150px; height: 150px;"/>
			</div>

		</form>
		<br />
		  <a href="/login" th:href="@{/login}" class="btn btn-info btn-lg">
          <span class="glyphicon glyphicon-chevron-left"></span> Login using  other social Providers
         </a>
		 
	</div>

</body>
</html>

Step 6 – Adding UserDetails service, our own implementation

This is the crucial step as would define here what to do when the user puts in his username/password combination.
If we want user authentication by a DAO class, we need to implement the UserDetailsService interface. This interface has loadUserByUsername() method which is used to validate the user, of course, we need to provide the implementation.

Remeber this method in Security config

@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
	}

Providing our own implementation of UserDetailsService

We will write the implementation of method loadUserByUsername.In this method, we will do couple of things

  • Find Username in DB, if not found throw an exception
  • If the user is found, login user and return the User object of type org.springframework.security.core.userdetails.User, spring will automatically update the Security context for us.

package com.login.security.service.impl;

import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.login.model.UserBean;
import com.login.repository.UserRepository;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private UserRepository userRepository;
	

	@Override
	@Transactional(readOnly = true)
	public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
		
		UserBean user = userRepository.findByEmail(email);
		 if (user == null) {
	            throw new UsernameNotFoundException("No user found with email: " + email);
	        }
		Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
		grantedAuthorities.add(new SimpleGrantedAuthority("LOGGED_USER"));
		return new User(user.getEmail(), user.getPassword(), grantedAuthorities);
		
		
	}

}

Step 7 – Setting Security context (auto login)

Once the user is logged in by social providers or by registering a user we need to update security context by setting the authentication

As per the spring API – “Authentication represents the token for an authentication request or for an authenticated principal once the request has been processed by the AuthenticationManager.authenticate(Authentication) method.
Once the request has been authenticated, the Authentication will usually be stored in a thread-local SecurityContext managed by the SecurityContextHolder by the authentication mechanism which is being used. An explicit authentication can be achieved, without using one of Spring Security’s authentication mechanisms, by creating an Authentication instance and using the code:

SecurityContextHolder.getContext().setAuthentication(authentication)

Which is exactly we are going to do in our method.

package com.login.autologin;

import java.util.HashSet;
import java.util.Set;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import com.login.model.UserBean;

@Service
public class Autologin {

    
    public void setSecuritycontext(UserBean userForm) {
   	Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
   	grantedAuthorities.add(new SimpleGrantedAuthority(userForm.getProvider().toUpperCase()));
   	Authentication authentication = new UsernamePasswordAuthenticationToken(userForm.getEmail(), userForm.getPassword(), grantedAuthorities);
   	SecurityContextHolder.getContext().setAuthentication(authentication);
       }
}

Step 8 – Changing the Login controller

Since now we have most of the things in place, we need to add the controller so when the registration form is submitted,

  • We need to save the user details on DB
  • Update the security context and set the authentication object
  • Redirect user to secure page.

Also, since in our Security config, we defined a “/login-error” path, we will handle that too in the same controller.

@Autowired
    private UserRepository userRepository;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private Autologin autologin;

    @PostMapping("/registration")
    public String registerUser(HttpServletResponse httpServletResponse, Model model, @Valid UserBean userBean, BindingResult bindingResult) {
	if (bindingResult.hasErrors()) {
	    return "registration";
	}
	userBean.setProvider("REGISTRATION");
	// Save the details in DB
	if (StringUtils.isNotEmpty(userBean.getPassword())) {
	    userBean.setPassword(bCryptPasswordEncoder.encode(userBean.getPassword()));
	}
	userRepository.save(userBean);

	autologin.setSecuritycontext(userBean);

	model.addAttribute("loggedInUser", userBean);
	return "secure/user";
    }

    /** If we can't find a user/email combination */
    @RequestMapping("/login-error")
    public String loginError(Model model) {
	model.addAttribute("loginError", true);
	return "login";
    }

Step 9 – Modifying the Social Providers

In previous post we had created the FacebookProvider, GoogleProvider and LinkedInProvider now we need to make some changes in them so they

  • Save the user details on DB
  • Update the security context and set the authentication object
  • Redirect the user to secure page.

In our class BaseProvider.java, we will add saveUserDetails and autoLoginUser method

@Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    protected Autologin autologin;

    protected void saveUserDetails(UserBean userBean) {
	if (StringUtils.isNotEmpty(userBean.getPassword())) {
	    userBean.setPassword(bCryptPasswordEncoder.encode(userBean.getPassword()));
	}
	userRepository.save(userBean);

    }

    public void autoLoginUser(UserBean userBean) {
	autologin.setSecuritycontext(userBean);
    }

In our provider classes(FacebookProvider, GoogleProvider and LinkedInProvider) we just need to add the code to

    Save the details in DB
    baseProvider.saveUserDetails(userForm);
    Login the User
    baseProvider.autoLoginUser(userForm);
    and also return back the secure page
    return "secure/user"

 

You will need to do these changes in all 3 classes(GoogleProvider, FaceBookProvider and LinkedInProvider)

public String getLinkedInUserData(Model model, UserBean userForm) {

		ConnectionRepository connectionRepository = baseProvider.getConnectionRepository();
		if (connectionRepository.findPrimaryConnection(LinkedIn.class) == null) {
			return REDIRECT_LOGIN;
		}
		populateUserDetailsFromLinkedIn(userForm);
		//Save the details in DB
		baseProvider.saveUserDetails(userForm);
		
		//Login the User
		baseProvider.autoLoginUser(userForm);
			
		model.addAttribute("loggedInUser",userForm);
		return "secure/user";
		}

Step 10 – Controller for /secure URL

Remeber, we have added in our 

defaultSuccessUrl("/secure/user")
SecurityConfig, well this is the page where we want the user to redirect, once they are authenticated.

Also on user/secure.html, we have defined

th:object="${loggedInUser}"
this attribute also needs to be initialized once the user is logged in by security config. So the question is how do we initialize the
"${loggedInUser}"
model attribute. If we don’t initialise it we will get an error.
An easy way to fix this problem is to use annotation called @ModelAttribute
@ModelAttribute("loggedInUser")
. If this annotation has been used all the RequestMapping method in the controller will be called only after the annotated method.

We can define a method where we will get the Authentication object and then use that object to find the user details.

@ModelAttribute("loggedInUser")
    public void secure(Model model) {
	Authentication auth = SecurityContextHolder.getContext().getAuthentication();
	UserBean user = userRepository.findByEmail(auth.getName());
	model.addAttribute("loggedInUser", user);
    }

Also, we need to define the method for mapping

@GetMapping("/secure/user")
 as shown below. Since we need to initialize model attribute
${loggedInUser}
only for
@GetMapping("/secure/user")
we need to define it in a separate controller class.

package com.login.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

import com.login.model.UserBean;
import com.login.repository.UserRepository;

@Controller
public class LoggedInUserController {

    @Autowired
    private UserRepository userRepository;

    @ModelAttribute("loggedInUser")
    public void secure(Model model) {
	Authentication auth = SecurityContextHolder.getContext().getAuthentication();
	UserBean user = userRepository.findByEmail(auth.getName());
	model.addAttribute("loggedInUser", user);
    }

    @GetMapping("/secure/user")
    public String securePage() {
	return "secure/user";
    }

}

Step 11 – Updating the properties file

Since we are using HSQLDB in this example and spring boot will configure most of the things for us, we don’t need to do more than defining a couple of properties for initializing the database. Add these properties in application.properties

spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true

Conclusion

Spring Social and Spring Security are very flexible and easily customizable for different use cases. They both can be used together to provide a seamless and smooth log in experience for users. The above code is just a step in explaining how both the offerings (Spring Social and Spring Security) can be used in unison.

For readers benefit, above code can be cloned from

This is how the project structure looks like

 

This is how the screen looks like after user is logged in, the logged in user and the roles are being displayed on top of screen. 

The post Part-2: Authorising user using Spring Social (Google, Facebook, LinkedIn) and Spring Security appeared first on Little Big Extra.

]]>
http://littlebigextra.com/part-2-authorising-user-using-spring-social-google-facebook-linkedin-spring-security/feed/ 5
Part -1 : Authorising user using Spring Social (Google, FaceBook and LinkedIn) and Spring Security http://littlebigextra.com/part-1-authorising-user-using-spring-social-google-facebook-and-linkedin-and-spring-security/ http://littlebigextra.com/part-1-authorising-user-using-spring-social-google-facebook-and-linkedin-and-spring-security/#comments Mon, 20 Nov 2023 14:30:21 +0000 http://littlebigextra.com/?p=1167 Part-1: Authorising user using Spring Social (Google, FaceBook and LinkedIn) Introduction Social Logins are becoming increasingly popular across web applications, they not only offer good user experience are safe and also saves the user from password fatigue. Imagine losing a customer because they can not log on to the application as they cannot remember a […]

The post Part -1 : Authorising user using Spring Social (Google, FaceBook and LinkedIn) and Spring Security appeared first on Little Big Extra.

]]>
Part-1: Authorising user using Spring Social (Google, FaceBook and LinkedIn)

Introduction

Social Logins are becoming increasingly popular across web applications, they not only offer good user experience are safe and also saves the user from password fatigue. Imagine losing a customer because they can not log on to the application as they cannot remember a password are not bothered to reset passwords etc. A social login is a kind of single sign-on where you use login information of a social network like Facebook, Twitter, Google+ to log on to a third website, instead of creating a new log-in account specially for that website.

Earlier I have written few blogs about Spring-Social features where I had written about few problems like no support for Google, only one user allowed by spring-social etc and also few exceptions which occur and then how to fix them.I thought of collating all the problems and writing a step by step tutorial explaining in detail, on how to register a user using spring social.

The main aim of this tutorial will be to integrate spring-security and spring-social together and create a web application where users can be registered either by

  • Registering their details
  • Google
  • Facebook
  • LinkedIn

In this first part of the tutorial, our aim will be to get data from spring-social and then display the details on the page, in the second part we will integrate the spring-security where we will ask the user to enter his details and then store on  DB.

Step 1- Maven Dependencies

Assuming that you know how to create a simple spring boot project, let us move on to add maven-dependencies required to run the project. If not, then you can create a spring-starter project and add thymleaf and web support.

Adding Spring-Social dependencies

The first step would be to add spring-social maven repositories, following  dependencies are  required

<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-core</artifactId>
			<version>2.0.0.M2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-config</artifactId>
			<version>2.0.0.M2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-facebook</artifactId>
			<version>3.0.0.M1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-linkedin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-google</artifactId>
			<version>1.0.0.RELEASE</version>
		</dependency>

In case you get the repository not available error, you might have to add the following repository to your pom.

<repositories>
		<repository>
			<id>alfresco-public</id>
			<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
		</repository>
	</repositories>

Adding Thymleaf, BootStrap, JQuery and Font-awesome dependencies

To make our web pages look nice we will add some Bootstrap CSS, Font-awesome and Jquery to them and also we will use thymleaf for HTML. Read Here if you want to know more in detail.

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			 <groupId>org.webjars</groupId>
			 <artifactId>jquery</artifactId>
			 <version>2.1.1</version>
		 </dependency>
		 <dependency>
			 <groupId>org.webjars</groupId>
			 <artifactId>bootstrap</artifactId>
			 <version>3.2.0</version>
		 </dependency>
		 <dependency>
			 <groupId>org.webjars</groupId>
			 <artifactId>webjars-locator</artifactId>
		 </dependency>
		 <dependency>
			 <groupId>org.webjars</groupId>
			 <artifactId>font-awesome</artifactId>
			 <version>4.7.0</version>
		 </dependency>

Complete POM

To avoid any mistakes, here is the complete POM.xml

<?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.login</groupId>
	<artifactId>SpringLogin</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>SpringLogin</name>
	<description>Spring Login - Example POC</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.8.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-core</artifactId>
			<version>2.0.0.M2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-config</artifactId>
			<version>2.0.0.M2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-facebook</artifactId>
			<version>3.0.0.M1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-linkedin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.social</groupId>
			<artifactId>spring-social-google</artifactId>
			<version>1.0.0.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> 
			</dependency> -->
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>3.1</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>2.1.1</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>bootstrap</artifactId>
			<version>3.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>webjars-locator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>font-awesome</artifactId>
			<version>4.7.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
		</dependency>

	</dependencies>
	<repositories>
		<repository>
			<id>alfresco-public</id>
			<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
		</repository>
	</repositories>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

 

Step 2- Adding Login Page and Controller

Once the dependencies have been resolved be will create a View and Controller

Adding View

To start we will create a view which will have 3 buttons each for Google, Facebook and Login. When the user will click on any of them he will be asked for authorization for the selected provider, upon verification his details will be rendered on the page.

Create a file under src/main/resources/templates/login.html with the following content. Please note that this view has 3 different forms each for different provider

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Login</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width" />
<base href="/" />
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/webjars/font-awesome/css/font-awesome.min.css"></link>
</head>

<body>

	<div class="container">
		<h1>Login Using</h1>

		<form action="/connect/google" method="POST" style="display: inline">
			<input type="hidden" name="scope" value="profile email" />
			<button type="submit" class="btn btn-danger">
				Google <span class="fa fa-google-plus"></span>
			</button>
		</form>

		<form action="/connect/facebook" method="POST" style="display: inline">
			<input type="hidden" name="scope" value="public_profile,email" />
			<button type="submit" class="btn btn-primary">
				Facebook <span class="fa fa-facebook"></span>
			</button>
		</form>

		<form action="/connect/linkedin" method="POST" style="display: inline">
			<input type="hidden" name="scope"
				value="r_basicprofile,r_emailaddress" />
			<button type="submit" class="btn btn-primary">
				LinkedIn <span class="fa fa-linkedin"></span>
			</button>
		</form>
	</div>
</body>
</html>

Adding Controller

Let us create a controller class called LoginController in package com.login.controller with a simple method which will map the above page to URL / or /login.

@Controller
public class LoginController {

@RequestMapping(value = { "/","/login" })
	public String login() {
		return "login";
	}
}

Now run the above application and at this point, a page like this should appear and of course, it won’t do anything yet.

Step 3- Creating a POJO to show authorised user detail

Once we verify the user via our social provider, we will show the user details on another page. We can store these details in a POJO called UserBean.
Let us define a class called UserBean.java as shown below in package com.login.model

package com.login.model;

import java.beans.Transient;
import java.io.Serializable;

public class UserBean implements Serializable{
	
	private static final long serialVersionUID = 1L;
	private String firstName;
	private String lastName;
	private String email;
	private String title;
	private String country;
	private String password;
	private String passwordConfirm;	
	private String provider;
	private String image;
	
	
	public String getEmail() {
		return email;
	}

	public String getImage() {
	    return image;
	}

	public void setImage(String image) {
	    this.image = image;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	
	public String getPasswordConfirm() {
		return passwordConfirm;
	}

	public void setPasswordConfirm(String passwordConfirm) {
		this.passwordConfirm = passwordConfirm;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}


	public String getProvider() {
		return provider;
	}

	public void setProvider(String provider) {
		this.provider = provider;
	}

	 

}

Step 4 – Creating Social Media Providers

For Facebook, Google and LinkedIn we will create a Provider class which will be used to do authentication and save user details to the UserBean, which can be later displayed on the page. To start off with we will create a BaseProvider which will have a constructor where all the providers will be initialized.

This BaseProvider is created by injecting the Facebook, Google, LinkedIn and ConnectionRepository repository. These objects are a reference to Spring Social’s Facebook, Google, LinkedIn and ConnectionRepository API binding.

package com.login.social.providers;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.google.api.Google;
import org.springframework.social.linkedin.api.LinkedIn;

@Configuration
@Scope(value = "request",  proxyMode = ScopedProxyMode.TARGET_CLASS)
public class BaseProvider {

	private Facebook facebook;
	private Google google;
	private LinkedIn linkedIn;
	private ConnectionRepository connectionRepository;
	
	public  BaseProvider(Facebook facebook,Google google,  LinkedIn linkedIn, ConnectionRepository connectionRepository) {
		this.facebook = facebook;
		this.connectionRepository = connectionRepository;
		this.google=google; 
		this.linkedIn= linkedIn;
	}

	public Facebook getFacebook() {
		return facebook;
	}

	public void setFacebook(Facebook facebook) {
		this.facebook = facebook;
	}

	public ConnectionRepository getConnectionRepository() {
		return connectionRepository;
	}

	public void setConnectionRepository(ConnectionRepository connectionRepository) {
		this.connectionRepository = connectionRepository;
	}

	public Google getGoogle() {
		return google;
	}

	public void setGoogle(Google google) {
		this.google = google;
	}

	public LinkedIn getLinkedIn() {
		return linkedIn;
	}

	public void setLinkedIn(LinkedIn linkedIn) {
		this.linkedIn = linkedIn;
	}
	
	

}

Now let us create provider class for each provider

Facebook Provider

We will use the below Facebook provider to authorize the user and then access Facebook Data. We will save the fetched user data into our UserBean.

package com.login.social.providers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.facebook.api.User;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

import com.login.model.UserBean;


@Service
public class FacebookProvider  {

	private static final String FACEBOOK = "facebook";
	private static final String REDIRECT_LOGIN = "redirect:/login";

    	@Autowired
    	BaseProvider baseProvider ;
    	

	public String getFacebookUserData(Model model, UserBean userForm) {

		ConnectionRepository connectionRepository = baseProvider.getConnectionRepository();
		if (connectionRepository.findPrimaryConnection(Facebook.class) == null) {
			return REDIRECT_LOGIN;
		}
		populateUserDetailsFromFacebook(userForm);
		model.addAttribute("loggedInUser",userForm);
		return "user";
	}

	protected void populateUserDetailsFromFacebook(UserBean userForm) {
		Facebook facebook = baseProvider.getFacebook();
		User user = facebook.userOperations().getUserProfile();
		userForm.setEmail(user.getEmail());
		userForm.setFirstName(user.getFirstName());
		userForm.setLastName(user.getLastName());
		userForm.setImage(user.getCover().getSource());
		userForm.setProvider(FACEBOOK);
	}

	 

}

Google Provider

We will use the below google provider to authorize the user and then access Google Data. We will save the fetched user data into our UserBean.

package com.login.social.providers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.google.api.Google;
import org.springframework.social.google.api.plus.Person;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

import com.login.model.UserBean;

@Service
public class GoogleProvider   {

	private static final String REDIRECT_CONNECT_GOOGLE = "redirect:/login";
	private static final String GOOGLE = "google";

   	@Autowired
    	BaseProvider socialLoginBean ;


	public String getGoogleUserData(Model model, UserBean userForm) {

		ConnectionRepository connectionRepository = socialLoginBean.getConnectionRepository();
		if (connectionRepository.findPrimaryConnection(Google.class) == null) {
			return REDIRECT_CONNECT_GOOGLE;
		}

		populateUserDetailsFromGoogle(userForm);
		model.addAttribute("loggedInUser",userForm);
		return "user";
	}

	
	protected void populateUserDetailsFromGoogle(UserBean userform) {
		Google google = socialLoginBean.getGoogle();
		Person googleUser = google.plusOperations().getGoogleProfile();
		userform.setEmail(googleUser.getAccountEmail());
		userform.setFirstName(googleUser.getGivenName());
		userform.setLastName(googleUser.getFamilyName());
		userform.setImage(googleUser.getImageUrl());
		userform.setProvider(GOOGLE);
	}

}

LinkedIn Provider

We will use the below LinkedIn provider to authorize the user and then accessLinkedIn Data. We will save the fetched user data into our UserBean.

package com.login.social.providers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.linkedin.api.LinkedIn;
import org.springframework.social.linkedin.api.LinkedInProfileFull;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

import com.login.model.UserBean;

@Service
public class LinkedInProvider  {

	private static final String LINKED_IN = "linkedIn";
	
	private static final String REDIRECT_LOGIN = "redirect:/login";

  	@Autowired
    	BaseProvider socialLoginBean ;

	public String getLinkedInUserData(Model model, UserBean userForm) {

		ConnectionRepository connectionRepository = socialLoginBean.getConnectionRepository();
		if (connectionRepository.findPrimaryConnection(LinkedIn.class) == null) {
			return REDIRECT_LOGIN;
		}
		populateUserDetailsFromLinkedIn(userForm);
		model.addAttribute("loggedInUser",userForm);
		return "user";
	}
	
	private void populateUserDetailsFromLinkedIn(UserBean userForm) {
		LinkedIn linkedIn = socialLoginBean.getLinkedIn();
		LinkedInProfileFull linkedInUser = linkedIn.profileOperations().getUserProfileFull();
		userForm.setEmail(linkedInUser.getEmailAddress());
		userForm.setFirstName(linkedInUser.getFirstName());
		userForm.setLastName(linkedInUser.getLastName());
		userForm.setImage(linkedInUser.getProfilePictureUrl());
		userForm.setProvider(LINKED_IN);
	}

}

Step 5 – Fixing some issues before we can run – Adding Google Support

If you would have noticed by now, spring social doesn’t support Google so to enable autoconfiguration of spring-social-google we need to

Add a GoogleAutoConfiguration Class

Spring social do have a spring-social-google project but it doesn’t have autoconfiguration for Google. So google authorization doesn’t work with spring boot autoconfigure straight away.

To enable autoconfigure of spring-social-google we need to

  • Add a GoogleAutoConfiguration
  • Add GoogleProperties

FacebookAutoConfiguration class you should be able to find it either in org.springframework.boot.autoconfigure.social package in spring-autoconfigure.jar does the autoconfiguration for Facebook, the cheat sheet or the trick would be to copy this File and replace Facebook with Google to enable GoogleAutoConfiguration

Alternatively, copy the below class and put in some package, make sure it is available in classpath(src/main/java or inside another source folder)

package com.login.config.google;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
import org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.social.config.annotation.EnableSocial;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactory;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.web.GenericConnectionStatusView;
import org.springframework.social.google.api.Google;
import org.springframework.social.google.connect.GoogleConnectionFactory;


@Configuration
@ConditionalOnClass({ SocialConfigurerAdapter.class, GoogleConnectionFactory.class })
@ConditionalOnProperty(prefix = "spring.social.google", name = "app-id")
@AutoConfigureBefore(SocialWebAutoConfiguration.class)
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class GoogleAutoConfiguration {

	@Configuration
	@EnableSocial
	@EnableConfigurationProperties(GoogleProperties.class)
	@ConditionalOnWebApplication
	protected static class GoogleConfigurerAdapter extends SocialAutoConfigurerAdapter {

		private final GoogleProperties properties;

		protected GoogleConfigurerAdapter(GoogleProperties properties) {
			this.properties = properties;
		}

		@Bean
		@ConditionalOnMissingBean(Google.class)
		@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
		public Google google(ConnectionRepository repository) {
			Connection<Google> connection = repository.findPrimaryConnection(Google.class);
			return connection != null ? connection.getApi() : null;
		}

		@Bean(name = { "connect/googleConnect", "connect/googleConnected" })
		@ConditionalOnProperty(prefix = "spring.social", name = "auto-connection-views")
		public GenericConnectionStatusView googleConnectView() {
			return new GenericConnectionStatusView("google", "Google");
		}

		@Override
		protected ConnectionFactory<?> createConnectionFactory() {
			return new GoogleConnectionFactory(this.properties.getAppId(), this.properties.getAppSecret());
		}

	}

}

 

Add GoogleProperties

In the same package add the below class, this is needed so when we add secret keys in properties file for google. We wont see any error.

package com.login.config.google;

import org.springframework.boot.autoconfigure.social.SocialProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "spring.social.google")

public class GoogleProperties extends SocialProperties{

	
}

Read Here: How to access Google data using spring-social

Step 6- Fixing some issues before we can run – Changing the default spring social redirect face flow

Spring Social is quick and easy to start but it needs some configuring before it can be used for production. The default flow for

The Views are predefined so the user flow is login.html -> connect/facebookConnected.html by default, irrespective of what our submit URL is, the facebook will override the flow and redirect you to connect/facebookConnected.html.

Read Here: How to change the default spring social redirect page flow 

We will add a ChangeDefaultFlowController  which will override the connectedView method and redirect the flow to “/facebook”, “/google” or “/LinkedIn”

package com.login.controller;

import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.web.ConnectController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
@RequestMapping("/connect")
public class ChangeDefaultFlowController extends ConnectController {

	public ChangeDefaultFlowController(ConnectionFactoryLocator connectionFactoryLocator,
			ConnectionRepository connectionRepository) {
		super(connectionFactoryLocator, connectionRepository);
	}
	
	@Override
    protected String connectedView(String providerId) {
        return "redirect:/"+providerId;
    }

}

 

Step 7 – Fixing some issues before we can run – Allowing Multiple Users to Login

If you test the Spring Facebook example, accessing FB data you will realise that it only supports one user. So when the first users log in to Facebook, only his details will be shared across all new sessions/users and they won’t be asked for any kind of authentication. One of the forum says that this example is supposed to demonstrate what can be done and is not intended for production use.

Read Here: Spring Social Facebook Authentication Example for multiple users 

To fix this problem you need to override a method which always returns a string called “anonymous”.The solution is to override the “anonymous�? as the UserId for each new user/session. So for each session, we can simply return a SessionID, however, it may not be unique enough to identify users, especially if it’sh being cached or stored somewhere in a connection database.
Using a Universally Unique Identifier(UUID) would be a safer bet.So we will store a new UUID in session so it persists between different requests but is alive only till the session is valid. See the below method which does the trick.

package com.login.identifier;

import java.util.UUID;

import org.springframework.context.annotation.Configuration;
import org.springframework.social.UserIdSource;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Configuration
public class SessionIdentifier extends SocialConfigurerAdapter
{

    @Override
    public UserIdSource getUserIdSource() {
        return new SessionIdUserIdSource();
    }

    private static final class SessionIdUserIdSource implements UserIdSource {
    	@Override
        public String getUserId() {
            RequestAttributes request = RequestContextHolder.currentRequestAttributes();
            String uuid = (String) request.getAttribute("_socialUserUUID", RequestAttributes.SCOPE_SESSION);
            if (uuid == null) {
                uuid = UUID.randomUUID().toString();
                request.setAttribute("_socialUserUUID", uuid, RequestAttributes.SCOPE_SESSION);
            }
            return uuid;
        }
    }
}

 

Step 8 – Updating the LoginController

Now since we have added all the fixes, its time to update the controller so our view can submit the forms to Controller

package com.login.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.login.model.UserBean;
import com.login.social.providers.FacebookProvider;
import com.login.social.providers.GoogleProvider;
import com.login.social.providers.LinkedInProvider;

@Controller
public class LoginController {

	@Autowired 
	FacebookProvider facebookProvider;
	
	@Autowired 
	GoogleProvider googleProvider;

	@Autowired 
	LinkedInProvider linkedInProvider;

	@RequestMapping(value = "/facebook", method = RequestMethod.GET)
	public String loginToFacebook(Model model) {
		return facebookProvider.getFacebookUserData(model, new UserBean());
	}

	@RequestMapping(value = "/google", method = RequestMethod.GET)
	public String loginToGoogle(Model model) {
		return googleProvider.getGoogleUserData(model, new UserBean());
	}

	@RequestMapping(value = "/linkedin", method = RequestMethod.GET)
	public String helloFacebook(Model model) {
		return linkedInProvider.getLinkedInUserData(model, new UserBean());
	}
	
	@RequestMapping(value = { "/","/login" })
	public String login() {
		return "login";
	}

}

Step 9 – Adding Spring properties

Assuming that you know how to register the API and public/private keys on Google/FB and LinkedIn for this example purpose you can use the below properties file.
Also, notice that server starts on port 3000(don’t change that, since the URL registered with these keys is localhost:3000

 

spring.social.facebook.appId=384261248599251
spring.social.facebook.appSecret=fd7fa1c5f5a267f463263a0ce7ff2025

spring.social.linkedin.app-id=771mrzk94hye1w
spring.social.linkedin.app-secret=iIJFgBf9lCb18zYe

spring.social.google.appId=12894100090-tqso3lih5o42isneort886la2pesafmp.apps.googleusercontent.com
spring.social.google.appSecret=9xfU16efvxQ-BTMsXT9wOLpw

server.port:3000

This is how the project structure will look like

Step 10 – User Details on page

To show the authenticated user on the page, we will display the UserBean using thymleaf. Add the below html page under src/main/resources/static/ as user.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Login</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width" />
<meta name="ctx" th:content="${#httpServletRequest.getContextPath()}" />
<base href="/" />
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/webjars/font-awesome/css/font-awesome.min.css"></link>
</head>
<body>



	<div class="container">
		<br /><br /><br />
		<form th:object="${loggedInUser}" method="post">
			<div class="row">
				<label for="username">Username:</label>
				<span th:text="${loggedInUser.email}" />
			</div>
			<div class="row">
				<label for="firstName">Name:</label>
				 <span th:text="*{firstName}" /> <span th:text="*{lastName}" />
			</div>
			<div class="row">
				<label for="image">Name:</label>
				 <img th:attr="src=@{*{image}}" style="width: 150px; height: 150px;"/>
			</div>

		</form>
		<br />
		  <a href="/login" th:href="@{/login}" class="btn btn-info btn-lg">
          <span class="glyphicon glyphicon-chevron-left"></span> Login using  other social Providers
         </a>
		 
	</div>

</body>
</html>

Now run the Project and access the application on http://localhost:3000/login.

You should be greeted by an authentication popup depending upon your provider selected and then you should be redirected to the page where your username, name, profile image will be displayed as below.

Conclusion

Spring Social API helps us in implementing an authentication mechanism with social providers. It is easy to use and we have seen how to configure it to tailor it to our needs.

Above code can be cloned from

In the next part we will use spring-security to register users and then only allow logged in users to navigate to secure pages. The link has been provided below for second part.

 

The post Part -1 : Authorising user using Spring Social (Google, FaceBook and LinkedIn) and Spring Security appeared first on Little Big Extra.

]]>
http://littlebigextra.com/part-1-authorising-user-using-spring-social-google-facebook-and-linkedin-and-spring-security/feed/ 10