如何使用 spring security 和 spring boot 对 Google 用户进行身份验证,将 mongoDB 作为存储库?

Posted

技术标签:

【中文标题】如何使用 spring security 和 spring boot 对 Google 用户进行身份验证,将 mongoDB 作为存储库?【英文标题】:How to authenticate Google users by using spring security and spring boot, having mongoDB as repository? 【发布时间】:2016-03-21 03:37:28 【问题描述】:

我是春天的新手。我正在使用 spring boot 以利用一些预定义的配置。好像很有用。

两个多星期以来,我一直坚持使用 spring security 创建身份验证和授权。我想使用 Google 用户进行身份验证。我使用 MongoDB 作为数据存储。

这是我与 Google 用户联系的代码,

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;
    

以上三个文件用于连接google。但是如何使用相同的代码进行身份验证。

正如我经历过的this tutorial (Follow Tutorial-1,2,3)。我发现了一些我们应该拥有的信息(如下),

所以我创建了以下类:

    实体模型 数据存储库 商务服务 用户详细信息服务 身份验证提供程序 安全配置

1.a) 实体模型(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) 实体模型(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。数据存储库(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) UserDteailsS​​ervice(UserAccountDetailsS​​ervice.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) UserDteailsS​​ervice(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.安全配置(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);
           
    

所以现在,如果我单独使用 Google 连接代码,我可以连接到 google。没关系,但是如何使用 Google 连接代码进行身份验证?

如果我的代码有误,请纠正我,如果我想包含或排除任何行或文件,请指导我,因为我是 spring 新手。

提前致谢

【问题讨论】:

【参考方案1】:

为什么要在自己的 UserDetailsS​​ervice 实现中再次检查密码?

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 !");

密码检查仅由谷歌身份提供者完成。所以你永远不会得到密码。 您是否已经检查过http://gabiaxel.github.io/spring-social-google-reference/connecting.html 和http://docs.spring.io/spring-social/docs/1.1.x/reference/htmlsingle/#enabling-provider-sign-in-with-code-socialauthenticationfilter-code

【讨论】:

如果您有任何来源,可以与我分享吗?我是一只新蜜蜂,所以理解上面的链接对我来说非常困难

以上是关于如何使用 spring security 和 spring boot 对 Google 用户进行身份验证,将 mongoDB 作为存储库?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring Security saml 的 IDP 会话超时

Spring security Saml - SP 和 IDP 的时间差

如何使用 Spring MVC & Security、Jasig CAS 和 JSP 视图在 Spring Boot 2 中配置 UTF-8?

在进行跨域请求时,如何使用 SockJS 和 STOMP 添加 Spring Security JSESSIONID?

在期待多个应用程序时,我应该使用 Spring Security SAML 还是 Shiboleth SP?

生成 SP 元数据时出现意外的堆栈跟踪表单 Spring-Security-SAML?