spring security oauth2 實(shí)現(xiàn)多用戶認(rèn)證

一、登陸獲取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 或 公證員手機(jī)號(hào)
     * @param var2  公證處id
     * @param var3  用戶類型
     * @return
     * @throws UsernameNotFoundException
     */
    UserDetails loadUserByUsername(String var1, String var2 ,String var3) throws UsernameNotFoundException;

}
# 實(shí)現(xiàn) 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());

    /**
     * 實(shí)現(xiàn) CustomUserDetailsService 自定義方法烫饼,獲取用戶
     * @param var1  微信id 或 公證員手機(jī)號(hào)
     * @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.獲取用戶 查詢數(shù)據(jù)庫中微信用戶
            PlUserEntity plUserEntity = plUserDao.getByWxOpenId(var1);

            if (plUserEntity == null) {
                throw new UsernameNotFoundException("用戶不存在嵌纲,請(qǐng)確認(rèn)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.獲取用戶可訪問權(quán)限信息 權(quán)限集合
            List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,gzy,zl");

            // 3.構(gòu)造UserDetails信息并返回 注意村怪,這里數(shù)據(jù)庫中存儲(chǔ)的是加密的,所以不需要加密;
            return new User(var1, password, authorityList);
        }else if(var3.equals(UserType.NPO_USER.code())){
            // 1.獲取用戶 查詢數(shù)據(jù)庫中公證員用戶
            PlNpoUserEntity plNpoUserEntity = plNpoUserDao.getUserForLogin(var2 , var1);

            if (plNpoUserEntity == null) {
                throw new UsernameNotFoundException("用戶不存在阿纤,請(qǐng)確認(rèn)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.獲取用戶可訪問權(quán)限信息 權(quán)限集合
            List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,gzy,zl");

            // 3.構(gòu)造UserDetails信息并返回 ,注意,這里數(shù)據(jù)庫中存儲(chǔ)的是未加密的八拱,所以這里需要加密阵赠;
            return new User(var1, new BCryptPasswordEncoder().encode(password), authorityList);
//            return new User(var1, password, authorityList);
        }else {
            throw new UsernameNotFoundException("用戶不存在,請(qǐng)確認(rèn)是否添加用戶類型 :" + 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();
    }

    /**
     * 認(rèn)證管理
     * @return 認(rèn)證管理對(duì)象
     * @throws Exception 認(rèn)證異常信息
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 允許匿名訪問所有接口 主要是 oauth 接口
     * @param http HTTP協(xié)議安全配置實(shí)例
     * @throws Exception 設(shè)置異常
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/miniapp/pl/**").permitAll()
                .and().authorizeRequests().antMatchers("/miniapp/npo/user").permitAll();
    }


    /**
     * 安全攔截機(jī)制(最重要)
     * @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)復(fù)制org.springframework.security.authentication.dao.DaoAuthenticationProvider 的代碼;
# 3)修改retrieveUser()方法爹谭,其他不需要?jiǎng)樱?

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;
    }

}
# 調(diào)用獲取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
http://www.reibang.com/p/fa5915a49cc3
http://www.reibang.com/p/45f9707b1792
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枷邪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诺凡,更是在濱河造成了極大的恐慌东揣,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腹泌,死亡現(xiàn)場(chǎng)離奇詭異嘶卧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)凉袱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門芥吟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人专甩,你說我怎么就攤上這事钟鸵。” “怎么了涤躲?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵棺耍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我种樱,道長(zhǎng)蒙袍,這世上最難降的妖魔是什么俊卤? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮左敌,結(jié)果婚禮上瘾蛋,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布涧狮。 她就那樣靜靜地躺著,像睡著了一般取董。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上无宿,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天茵汰,我揣著相機(jī)與錄音,去河邊找鬼孽鸡。 笑死蹂午,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的彬碱。 我是一名探鬼主播豆胸,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼巷疼!你這毒婦竟也來了晚胡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤嚼沿,失蹤者是張志新(化名)和其女友劉穎估盘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骡尽,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡遣妥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了攀细。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箫踩。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辨图,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肢藐,我是刑警寧澤故河,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站吆豹,受9級(jí)特大地震影響鱼的,放射性物質(zhì)發(fā)生泄漏理盆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一凑阶、第九天 我趴在偏房一處隱蔽的房頂上張望猿规。 院中可真熱鬧,春花似錦宙橱、人聲如沸姨俩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽环葵。三九已至,卻和暖如春宝冕,著一層夾襖步出監(jiān)牢的瞬間张遭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工地梨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菊卷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓宝剖,卻偏偏與公主長(zhǎng)得像洁闰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诈闺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容