简体   繁体   中英

How to authenticate Google users by using spring security and spring boot, having mongoDB as repository?

I am new to spring. I'm using spring boot in order to avail some predefined configurations. It seems to be useful.

For more than 2-weeks I'm stuck with creating an Authentication and Authorization using spring security. I would like to use Google users for authentication. And I'm using MongoDB as data storage.

Here's my code for connecting with Google users,

1. SocialConnectionConfiguration.java

package com.example.social;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.social.google.connect.GoogleConnectionFactory;

@Configuration
public class SocialConnectionConfiguration {

    // Google Application Credentials
    private static final String GoogleClientID = "<clientID>";
    private static final String GoogleClientSecret = "<clientSecret>";

    @Bean
    public GoogleConnectionFactory getGoogleConnectionFactory() {
        GoogleConnectionFactory connectionFactory = 
                new GoogleConnectionFactory(GoogleClientID, GoogleClientSecret);
        return connectionFactory;
    }

}

2. SocialConnectionConfiguration.java

package com.example.social;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.social.google.connect.GoogleConnectionFactory;

@Configuration
public class SocialConnectConfiguration{

    @Autowired
    GoogleConnectionFactory gplusConnectionFactory;

    @Bean
    public GoogleAPI getGoogleAPI(){
        GoogleAPI googleAPI  = new GoogleAPI(gplusConnectionFactory);
        return googleAPI;
    }
}

3. GoogleAPI.java

package com.example.social;

import org.springframework.social.connect.Connection;
import org.springframework.social.google.api.Google;
import org.springframework.social.google.connect.GoogleConnectionFactory;
import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.oauth2.GrantType;
import org.springframework.social.oauth2.OAuth2Parameters;

public class GoogleAPI {

    private GoogleConnectionFactory GplusConnectionFactory;
    private Google google;
    private static final String REDIRECT_URI = "http://localhost:8080/google_response";

    public Google getGoogle() {
        return google;
    }

    public GoogleAPI(GoogleConnectionFactory GplusConnectionFactory) {
        this.GplusConnectionFactory = GplusConnectionFactory;
    }

    public String getRedirectURI() {

        OAuth2Parameters params = new OAuth2Parameters();
        params.setRedirectUri(REDIRECT_URI);
        params.setScope("profile");
        params.setScope("openid");
        params.setScope("email");
        String authorizeUrl = GplusConnectionFactory.getOAuthOperations().buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, params);
        return authorizeUrl;

    }

    public Google establishFacebookConnection(String accessToken) {
        AccessGrant accessGrant = GplusConnectionFactory.getOAuthOperations().exchangeForAccess(accessToken,REDIRECT_URI, null);
        Connection<Google> connection = GplusConnectionFactory.createConnection(accessGrant);
        google =  connection.getApi();
        return google;
    }

    public boolean isAuthorized() {
        if(google != null){
            return google.isAuthorized();
        }
        return false;
    }
}

The above three files are used to get connect with google. But how can I give authentication with same code.

As I have gone through this tutorial (Follow Tutorial-1,2,3) . I have come across some information's (down below) that we should have,

So I have created the below classes :

  1. An Entity model
  2. Data repository
  3. Business Service
  4. UserDetailsService
  5. Authentication Provider
  6. Security Configuration

1.a) Entity Model (UserAccount.java)

package com.example.model;

import java.util.Set;

import javax.validation.constraints.NotNull;

import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection="user_account")
public class UserAccount {

    @Id
    private ObjectId id;

    private String firstName;

    @Indexed
    private String lastName;

    private Integer age;

    @Indexed
    @NotNull
    private final String username;

    @NotNull
    private String password;

    @NotNull
    private boolean enabled = true;

    @NotNull
    private boolean credentialsexpired = false;

    @NotNull
    private boolean expired = false;

    @NotNull
    private boolean locked = false;

    private Set<Role> roles;

    @PersistenceConstructor
    public UserAccount(String username, String firstName, String lastName, Integer age) {
        this.username = username;
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }


    public ObjectId getId() {
        return id;
    }

    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 Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

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

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public boolean isCredentialsexpired() {
        return credentialsexpired;
    }

    public void setCredentialsexpired(boolean credentialsexpired) {
        this.credentialsexpired = credentialsexpired;
    }

    public boolean isExpired() {
        return expired;
    }

    public void setExpired(boolean expired) {
        this.expired = expired;
    }

    public boolean isLocked() {
        return locked;
    }

    public void setLocked(boolean locked) {
        this.locked = locked;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "[" + this.getId() +
                " : " + this.getUsername() +
                " : " + this.firstName +
                " : " + this.getLastName() +
                " : " + this.getAge().toString()+"]";
    }

}

1.b) Entity Model (Role.java)

package com.example.model;

import javax.validation.constraints.NotNull;

public class Role {

    private static final String ADMIN = "ADMIN";
    private static final String MANAGER = "MANAGER";
    private static final String USER = "USER";

    @NotNull
    private String code;

    @NotNull
    private String label;

    public Role() {

    }

    public Role(String code) {
        this.code = code;
        assignRole(code);
    }

    private void assignRole(String code) {
        if(code.equals("1")) {
            this.label = ADMIN;
        }
        else if(code.equals("2")) {
            this.label = MANAGER;
        }
        else if(code.equals("3")) {
            this.label = USER;
        }

    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

}

2. Data Repository(UserAccountRepository.java)

package com.example.repository;

import java.io.Serializable;

import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import com.example.model.UserAccount;

@Repository
public interface UserAccountRepository extends MongoRepository<UserAccount, Serializable> {

    public UserAccount findById(ObjectId id);

    public UserAccount findByUsername(String username);

}

3.a) UserDteailsService(UserAccountDetailsService.java)

package com.example.secutiry;

import java.util.ArrayList;
import java.util.Collection;

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 com.example.model.Role;
import com.example.model.UserAccount;
import com.example.service.UserAccountService;

@Service
public class UserAccountDetailsService implements UserDetailsService{

    @Autowired
    private UserAccountService userService;

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

        UserAccount user = userService.findByUsername(username);

        if(user == null) {
            throw new UsernameNotFoundException("Given user name doesn't match !");
        }

        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();

        for (Role role : user.getRoles()) {
            grantedAuthorities.add(new SimpleGrantedAuthority(role.getLabel()));
        }

        User userDetails = new User(user.getUsername(),
                user.getPassword(),user.isEnabled(),
                user.isExpired(),user.isCredentialsexpired(),
                user.isLocked(), grantedAuthorities);

        return userDetails;
    }

}

3.b) UserDteailsService(UserAccountService.java)

package com.example.service;

import java.io.Serializable;
import java.util.List;

import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import com.example.model.UserAccount;
import com.example.repository.UserAccountRepository;

@Service
public class UserAccountService implements UserAccountRepository {

    @Autowired
    private UserAccountRepository repository;

    @Override
    public <S extends UserAccount> List<S> save(Iterable<S> entites) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<UserAccount> findAll() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<UserAccount> findAll(Sort sort) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <S extends UserAccount> S insert(S entity) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <S extends UserAccount> List<S> insert(Iterable<S> entities) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Page<UserAccount> findAll(Pageable pageable) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <S extends UserAccount> S save(S entity) {
        repository.save(entity);
        return null;
    }

    @Override
    public UserAccount findOne(Serializable id) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean exists(Serializable id) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Iterable<UserAccount> findAll(Iterable<Serializable> ids) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long count() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void delete(Serializable id) {
        // TODO Auto-generated method stub

    }

    @Override
    public void delete(UserAccount entity) {
        // TODO Auto-generated method stub

    }

    @Override
    public void delete(Iterable<? extends UserAccount> entities) {
        // TODO Auto-generated method stub

    }

    @Override
    public void deleteAll() {
        // TODO Auto-generated method stub

    }

    @Override
    public UserAccount findById(ObjectId id) {
        UserAccount user = repository.findById(id);
        return user;
    }

    @Override
    public UserAccount findByUsername(String username) {
        UserAccount user = repository.findByUsername(username);
        return user;
    }

}

4. AuthenticationProvider (UserAccountAuthenticationProvider.java)

package com.example.secutiry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class UserAccountAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    @Autowired
    private UserAccountDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken token)
            throws AuthenticationException {

        if(token.getCredentials() == null
                || userDetails.getPassword() == null) {
            throw new BadCredentialsException("Credentials may not be Empty!");
        }

        if(!passwordEncoder.matches(token.getCredentials().toString(),
                userDetails.getPassword())) {
            throw new BadCredentialsException("Invalid Credentials !");
        }

    }

    @Override
    protected UserDetails retrieveUser(String username, 
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        return userDetails;
    }

}

5. Security Configuration(SecurityConfiguration.java)

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.example.secutiry.UserAccountAuthenticationProvider;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Autowired
    private UserAccountAuthenticationProvider userAccountAuthenticationProvider;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(userAccountAuthenticationProvider);
    }

    @Configuration
    @Order(1)
    public static class ApiWebSecurityConfigurationAdapter
                extends WebSecurityConfigurerAdapter{

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http
                .antMatcher("/**")
                    .authorizeRequests()
                        .anyRequest().hasRole("USER")
                .and()
                .httpBasic()
                .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }   
    }
}

So now, If I use the Google connectivity code separately I can able to connect to google. That's fine, but how can I use the Google connectivity code to authenticate ?

If my code is wrong please correct me, If I'am suppose to include or exclude any line or file please guide me since I'm new to spring.

Thanks in advance

Why do you check password again in your own UserDetailsService implementation?

if(token.getCredentials() == null
            || userDetails.getPassword() == null) {
        throw new BadCredentialsException("Credentials may not be Empty!");
}
if(!passwordEncoder.matches(token.getCredentials().toString(),
        userDetails.getPassword())) {
        throw new BadCredentialsException("Invalid Credentials !");
}

Password check is done by google identity provider only. So you'll never will get a password. Did you already check http://gabiaxel.github.io/spring-social-google-reference/connecting.html and http://docs.spring.io/spring-social/docs/1.1.x/reference/htmlsingle/#enabling-provider-sign-in-with-code-socialauthenticationfilter-code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM