美文网首页
spring security oauth2 实现多用户认证

spring security oauth2 实现多用户认证

作者: 一介书生独醉江湖 | 来源:发表于2023-05-04 13:59 被阅读0次

一、登陆获取token

# 新增 CustomUserDetailsService 接口并继承 UserDetailsService

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * @Author ds
 * @Date 2023/4/28
 */
public interface CustomUserDetailsService extends UserDetailsService {

    /**
     * 继承原来的 UserDetailsService 新增自定义方法
     * @param var1  微信id 或 公证员手机号
     * @param var2  公证处id
     * @param var3  用户类型
     * @return
     * @throws UsernameNotFoundException
     */
    UserDetails loadUserByUsername(String var1, String var2 ,String var3) throws UsernameNotFoundException;

}
# 实现 CustomUserDetailsService 自定义方法

import com.deepnotary.dict.UserType;
import com.deepnotary.enotary.dao.PlNpoUserDao;
import com.deepnotary.enotary.dao.PlUserDao;
import com.deepnotary.enotary.entity.PlNpoUserEntity;
import com.deepnotary.enotary.entity.PlUserEntity;
import com.deepnotary.enotary.user.service.CustomUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Author ds
 * @Date 2023/4/28
 */
@Slf4j
@Component
public class UserDetailsServiceImpl implements CustomUserDetailsService {

    @Autowired
    private PlUserDao plUserDao;

    @Autowired
    private PlNpoUserDao plNpoUserDao;

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 实现 CustomUserDetailsService 自定义方法,获取用户
     * @param var1  微信id 或 公证员手机号
     * @param var2  公证处id
     * @param var3  用户类型
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String var1, String var2 ,String var3) throws UsernameNotFoundException {
        logger.info("miniapp-oauth loadUserByUsername() : var1 : {} , va2 : {}, va3 : {}" , var1 , var2 , var3);
        // 微信用户
        if(var3.equals(UserType.WX_USER.code())){
            // 1.获取用户 查询数据库中微信用户
            PlUserEntity plUserEntity = plUserDao.getByWxOpenId(var1);

            if (plUserEntity == null) {
                throw new UsernameNotFoundException("用户不存在,请确认openId是否正确:" + var1);
            }
            String password = plUserEntity.getPassword();
            logger.info(UserType.WX_USER.message() + " password : " + password);
            logger.info(UserType.WX_USER.message() + " ecrypt password : " + new BCryptPasswordEncoder().encode(password));

            // 2.获取用户可访问权限信息 权限集合
            List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,gzy,zl");

            // 3.构造UserDetails信息并返回 注意,这里数据库中存储的是加密的,所以不需要加密;
            return new User(var1, password, authorityList);
        }else if(var3.equals(UserType.NPO_USER.code())){
            // 1.获取用户 查询数据库中公证员用户
            PlNpoUserEntity plNpoUserEntity = plNpoUserDao.getUserForLogin(var2 , var1);

            if (plNpoUserEntity == null) {
                throw new UsernameNotFoundException("用户不存在,请确认cellphone是否正确:" + var1);
            }

            String password = plNpoUserEntity.getPasswd();
            logger.info(UserType.NPO_USER.message() + " password : " + password);
            logger.info(UserType.NPO_USER.message() + " ecrypt password : " + new BCryptPasswordEncoder().encode(password));
            // 2.获取用户可访问权限信息 权限集合
            List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,gzy,zl");

            // 3.构造UserDetails信息并返回 ,注意,这里数据库中存储的是未加密的,所以这里需要加密;
            return new User(var1, new BCryptPasswordEncoder().encode(password), authorityList);
//            return new User(var1, password, authorityList);
        }else {
            throw new UsernameNotFoundException("用户不存在,请确认是否添加用户类型 :" + var2);
        }
    }

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        return null;
    }
}
# 替换所有 userDetailsService 为 CustomUserDetailsService



import com.deepnotary.enotary.user.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * 自定义web安全配置类
 */
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public CustomUserDetailsService customUserDetailsService;

    @Bean(name="customAuthenticationProvider")
    public AuthenticationProvider customAuthenticationProvider() {
        CustomAuthenticationProvider customAuthenticationProvider= new CustomAuthenticationProvider();
        customAuthenticationProvider.setUserDetailsService(customUserDetailsService);
        customAuthenticationProvider.setHideUserNotFoundExceptions(false);
        customAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return customAuthenticationProvider;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider());
    }

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

    /**
     * 认证管理
     * @return 认证管理对象
     * @throws Exception 认证异常信息
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 允许匿名访问所有接口 主要是 oauth 接口
     * @param http HTTP协议安全配置实例
     * @throws Exception 设置异常
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/miniapp/pl/**").permitAll()
                .and().authorizeRequests().antMatchers("/miniapp/npo/user").permitAll();
    }


    /**
     * 安全拦截机制(最重要)
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) {
        // 放行
        web.ignoring().antMatchers(
                "/error",
                "/**/**.html",
                "/static/**",
                "/v2/api-docs/**",
                "/swagger-resources/**",
                "/webjars/**",
                "/favicon.ico",
                "/miniapp-oauth/**",
                "/test/**",
                "/miniapp/wx/onLogin"
        );
    }

}

# 1)自定义 CustomAuthenticationProvider,并继承 AbstractUserDetailsAuthenticationProvider ;
# 2)复制org.springframework.security.authentication.dao.DaoAuthenticationProvider 的代码;
# 3)修改retrieveUser()方法,其他不需要动;


import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;

/**
 * @Author ds
 * @Date 2023/5/4
 */
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
    private PasswordEncoder passwordEncoder;
    private volatile String userNotFoundEncodedPassword;
    private UserDetailsService userDetailsService;
    private UserDetailsPasswordService userDetailsPasswordService;

    public CustomAuthenticationProvider() {
        this.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
    }

    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            this.logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String presentedPassword = authentication.getCredentials().toString();
            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                this.logger.debug("Authentication failed: password does not match stored value");
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }

    protected void doAfterPropertiesSet() throws Exception {
        Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
    }

    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        this.prepareTimingAttackProtection();

        try {
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            } else {
                return loadedUser;
            }
        } catch (UsernameNotFoundException var4) {
            this.mitigateAgainstTimingAttack(authentication);
            throw var4;
        } catch (InternalAuthenticationServiceException var5) {
            throw var5;
        } catch (Exception var6) {
            throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
        }
    }

    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        boolean upgradeEncoding = this.userDetailsPasswordService != null && this.passwordEncoder.upgradeEncoding(user.getPassword());
        if (upgradeEncoding) {
            String presentedPassword = authentication.getCredentials().toString();
            String newPassword = this.passwordEncoder.encode(presentedPassword);
            user = this.userDetailsPasswordService.updatePassword(user, newPassword);
        }

        return super.createSuccessAuthentication(principal, authentication, user);
    }


    private void prepareTimingAttackProtection() {
        if (this.userNotFoundEncodedPassword == null) {
            this.userNotFoundEncodedPassword = this.passwordEncoder.encode("userNotFoundPassword");
        }

    }

    private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
        if (authentication.getCredentials() != null) {
            String presentedPassword = authentication.getCredentials().toString();
            this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
        }

    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
        this.passwordEncoder = passwordEncoder;
        this.userNotFoundEncodedPassword = null;
    }

    protected PasswordEncoder getPasswordEncoder() {
        return this.passwordEncoder;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    protected UserDetailsService getUserDetailsService() {
        return this.userDetailsService;
    }

    public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
        this.userDetailsPasswordService = userDetailsPasswordService;
    }

}
# 调用获取token接口

{
  "status": 0,
  "message": "登录成功",
  "recvTime": "20230505134843114",
  "respTime": "20230505134843517",
  "extendData": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJkcyIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2ODM4NzAzODQsImF1dGhvcml0aWVzIjpbImFkbWluIiwiZ3p5IiwiemwiXSwianRpIjoiOTc5ZDVkODUtMDA2Ni00NDg0LWE2ZDctYzlhYTc0M2Q5ZTBlIiwiY2xpZW50X2lkIjoidXNlci1jbGllbnQiLCJ1c2VybmFtZSI6ImRzIn0.auIMJJL6wZY6a4sCbZUINCDVa4QpgzDd3NEOSc1pBw4"
  }
}
# 参考
https://www.cnblogs.com/dw3306/p/13533973.html
https://blog.csdn.net/weixin_43909881/article/details/104925068
https://www.jianshu.com/p/fa5915a49cc3
https://www.jianshu.com/p/45f9707b1792

相关文章

网友评论

      本文标题:spring security oauth2 实现多用户认证

      本文链接:https://www.haomeiwen.com/subject/eufijdtx.html