欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

spring?security?自定義Provider?如何實(shí)現(xiàn)多種認(rèn)證

 更新時(shí)間:2021年12月20日 09:59:49   作者:yfx000  
這篇文章主要介紹了spring?security?自定義Provider實(shí)現(xiàn)多種認(rèn)證方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

我的系統(tǒng)里有兩種用戶,對(duì)應(yīng)數(shù)據(jù)庫兩張表,所以必須自定義provider 和 AuthenticationToken,這樣才能走到匹配自定義的UserDetailsService。

必須自定義原因在于,security內(nèi)部是遍歷prodvider,根據(jù)其support 方法判斷是否匹配Controller提交的token,然后走provider注入的認(rèn)證service方法。

security內(nèi)部認(rèn)證流程是這樣的

1、 Controller

用用戶名和密碼構(gòu)造AuthenticationToken 并提交給 authenticationManager,

authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));

2、spring security

會(huì)遍歷自定義和內(nèi)置provider,根據(jù)provider的support方法判斷入?yún)oken所匹配provider

public boolean supports(Class<?> authentication) {
   return (EcStaffUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}

3、調(diào)用匹配的provider內(nèi)部認(rèn)證邏輯

過程中會(huì)調(diào)用UserDetailsService.loadUserByUsername,這個(gè)service可以在SecurityConfig中配置注入到provider

4、UserDetailsService

需要我們自己查詢數(shù)據(jù)庫中用戶對(duì)象,返回對(duì)象UserDetails,

我返回的是LoginUser ( implements UserDetails ),這樣把數(shù)據(jù)庫查出來用戶對(duì)象加進(jìn)去,方便前臺(tái)Controller使用

@Override
public UserDetails loadUserByUsername(String username) //查詢數(shù)據(jù)庫

5、繼續(xù)走spring security內(nèi)部邏輯

包括判斷密碼是否匹配等,如果密碼不匹配或帳號(hào)過期等spring會(huì)上拋異常到Controller

6、所有調(diào)用完畢就會(huì)

回到Controller的方法,并返回authentication。對(duì)于異常需要自己捕獲,詳情可參見后面的代碼。

authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();

說明:

大部分人是在流程最前面使用filter實(shí)現(xiàn)各種校驗(yàn),而我的項(xiàng)目全部是前后端分離,所以我的filter只校驗(yàn)token有效性,我把各種非空校驗(yàn)放在controller。

1、基礎(chǔ)配置-SecurityConfig

    @Autowired
    @Qualifier("userDetailsServiceImpl")
    private UserDetailsService userDetailsService;
    
    @Autowired
    @Qualifier("ecStaffDetailsServiceImpl")
    private UserDetailsService ecStaffDetailsServiceImpl;
 
    /**
     * token認(rèn)證過濾器
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;
    
    /**
     * 解決 無法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        return super.authenticationManagerBean();
    }
 
    /**
     * anyRequest          |   匹配所有請(qǐng)求路徑
     * access              |   SpringEl表達(dá)式結(jié)果為true時(shí)可以訪問
     * anonymous           |   匿名可以訪問
     * denyAll             |   用戶不能訪問
     * fullyAuthenticated  |   用戶完全認(rèn)證可以訪問(非remember-me下自動(dòng)登錄)
     * hasAnyAuthority     |   如果有參數(shù),參數(shù)表示權(quán)限,則其中任何一個(gè)權(quán)限可以訪問
     * hasAnyRole          |   如果有參數(shù),參數(shù)表示角色,則其中任何一個(gè)角色可以訪問
     * hasAuthority        |   如果有參數(shù),參數(shù)表示權(quán)限,則其權(quán)限可以訪問
     * hasIpAddress        |   如果有參數(shù),參數(shù)表示IP地址,如果用戶IP和參數(shù)匹配,則可以訪問
     * hasRole             |   如果有參數(shù),參數(shù)表示角色,則其角色可以訪問
     * permitAll           |   用戶可以任意訪問
     * rememberMe          |   允許通過remember-me登錄的用戶訪問
     * authenticated       |   用戶登錄后可訪問
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        httpSecurity
                // CRSF禁用,因?yàn)椴皇褂胹ession
                .csrf().disable()
                // 認(rèn)證失敗處理類
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 過濾請(qǐng)求
                .authorizeRequests()
                // 對(duì)于登錄login 驗(yàn)證碼captchaImage 允許匿名訪問
                .antMatchers("/login", "/captchaImage", "/store-api/ecommerce/login/**").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/profile/**").anonymous()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/common/download/resource**").anonymous()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                // 除上面外的所有請(qǐng)求全部需要鑒權(quán)認(rèn)證
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();
        httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    } 
    
    /**
     * 強(qiáng)散列哈希加密實(shí)現(xiàn)
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
 
    /**
     * 身份認(rèn)證接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
		//自定義provider及service,一套身份認(rèn)證
        auth.authenticationProvider(getEcStaffUsernamePasswordAuthenticationProvider())
		//使用系統(tǒng)自帶provider,及自定義service,另一套認(rèn)證
            .userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }    
	 /**
     * 自定義provider,注入自定義service
     */
    public EcStaffUsernamePasswordAuthenticationProvider getEcStaffUsernamePasswordAuthenticationProvider() {
        EcStaffUsernamePasswordAuthenticationProvider provider = new EcStaffUsernamePasswordAuthenticationProvider();
        provider.setPasswordEncoder(bCryptPasswordEncoder());
        provider.setUserDetailsService(ecStaffDetailsServiceImpl);
        return provider;
    }

2、基礎(chǔ)配置-自定義AuthenticationToken

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
public class EcStaffUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken{
    public EcStaffUsernamePasswordAuthenticationToken(Object principal, Object credentials) {
        super(principal, credentials);
    }
    private static final long serialVersionUID = 8665690993060353849L;   
}

3、基礎(chǔ)配置-自定義provider

import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 
import com.ruoyi.framework.security.authToken.EcStaffUsernamePasswordAuthenticationToken;
public class EcStaffUsernamePasswordAuthenticationProvider extends DaoAuthenticationProvider{
    public boolean supports(Class<?> authentication) {
        return (EcStaffUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }
}

4、Controller發(fā)起身份認(rèn)證

        // 用戶驗(yàn)證
        Authentication authentication = null;
        try
        {
            // 該方法會(huì)去調(diào)用EcStaffDetailsServiceImpl.loadUserByUsername
            // 因?yàn)檫@個(gè)自定token只被自定provider的support所支持
            // 所以才會(huì)provider中注入的EcStaffDetailsServiceImpl,在security配置文件注入的
            authentication = authenticationManager.authenticate(new EcStaffUsernamePasswordAuthenticationToken(username, password));
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {        
                //密碼不匹配,需自定義返回前臺(tái)消息
                throw new UserPasswordNotMatchException();
            }
            else
            {
                throw new CustomException(e.getMessage());
            }
        }        
        //登錄成功
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();

5、service查詢數(shù)據(jù)庫中用戶對(duì)象

import java.util.HashSet;
import java.util.Set; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
 
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.ecommerce.constant.StaffStatusConstant;
import com.ruoyi.ecommerce.domain.EcStaff;
import com.ruoyi.ecommerce.service.IEcStaffService;
import com.ruoyi.framework.security.LoginUser;
 
/**
 * 用戶驗(yàn)證處理
 */
@Service
public class EcStaffDetailsServiceImpl implements UserDetailsService
{
    private static final Logger log = LoggerFactory.getLogger(EcStaffDetailsServiceImpl.class); 
    @Autowired
    private IEcStaffService ecStaffService; 
    @Autowired
    private SysPermissionService permissionService; 
    @Override
    public UserDetails loadUserByUsername(String username)
    {
        QueryWrapper<EcStaff> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", username);
        EcStaff user = ecStaffService.getOne(queryWrapper);
        
        if (StringUtils.isNull(user))
        {
            log.info("登錄用戶:{} 不存在.", username);
            throw new BaseException(MessageUtils.message("user.not.exists"));
        }
        else if (Constants.DELETED.equals(user.getDeleted()))
        {
            log.info("登錄用戶:{} 已被刪除.", username);
            throw new BaseException(MessageUtils.message("user.password.delete"));
        }
        return createLoginUser(user);
    }
 
    /**
     * 查詢用戶權(quán)限
     * @param user
     * @return
     */
    public UserDetails createLoginUser(EcStaff user)
    {
        return new LoginUser(user, permissionService.getMenuPermission(user));        
    }
}

6、service返回的LoginUser

因?yàn)橛袃煞N用戶sysuser和ecstaff,為了基于這個(gè)LoginUser統(tǒng)一提供getUsername方法,讓他們繼承或?qū)崿F(xiàn)統(tǒng)一BaseUser,

可以不統(tǒng)一封裝因?yàn)長oginUser構(gòu)造方法入?yún)⑹莖bject , 即LoginUser(Object user, Set<String> permissions)

import java.util.Collection;
import java.util.Set; 
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; 
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ruoyi.ecommerce.domain.BaseUser;
 
/**
 * 登錄用戶身份權(quán)限
 * 
 * @author ruoyi
 */
public class LoginUser implements UserDetails
{
    private static final long serialVersionUID = 1L;
 
    /**
     * 用戶唯一標(biāo)識(shí)
     */
    private String token;
 
    /**
     * 登陸時(shí)間
     */
    private Long loginTime;
 
    /**
     * 過期時(shí)間
     */
    private Long expireTime;
 
    /**
     * 登錄IP地址
     */
    private String ipaddr;
 
    /**
     * 登錄地點(diǎn)
     */
    private String loginLocation;
 
    /**
     * 瀏覽器類型
     */
    private String browser;
 
    /**
     * 操作系統(tǒng)
     */
    private String os;
 
    /**
     * 權(quán)限列表
     */
    private Set<String> permissions;
 
    /**
     * 用戶信息
     */
    private Object user;
    /**
     * 用戶的class
     */
    private Class userClass;
 
    public String getToken()
    {
        return token;
    }
 
    public void setToken(String token)
    {
        this.token = token;
    }
 
    public LoginUser()
    {
    }
 
    public LoginUser(Object user, Set<String> permissions)
    {
        this.userClass = user.getClass();
        this.user = user;
        this.permissions = permissions;
    }
 
    @JsonIgnore
    @Override
    public String getPassword()
    {
        return ((BaseUser)user).getPassword();
    }
 
    @Override
    public String getUsername()
    {
        return ((BaseUser)user).getUserName();
    }
 
    /**
     * 賬戶是否未過期,過期無法驗(yàn)證
     */
    @JsonIgnore
    @Override
    public boolean isAccountNonExpired()
    {
        return true;
    }
 
    /**
     * 指定用戶是否解鎖,鎖定的用戶無法進(jìn)行身份驗(yàn)證
     * 
     * @return
     */
    @JsonIgnore
    @Override
    public boolean isAccountNonLocked()
    {
        return true;
    }
 
    /**
     * 指示是否已過期的用戶的憑據(jù)(密碼),過期的憑據(jù)防止認(rèn)證
     * 
     * @return
     */
    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired()
    {
        return true;
    }
 
    /**
     * 是否可用 ,禁用的用戶不能身份驗(yàn)證
     * 
     * @return
     */
    @JsonIgnore
    @Override
    public boolean isEnabled()
    {
        return true;
    }
 
    public Long getLoginTime()
    {
        return loginTime;
    }
 
    public void setLoginTime(Long loginTime)
    {
        this.loginTime = loginTime;
    }
 
    public String getIpaddr()
    {
        return ipaddr;
    }
 
    public void setIpaddr(String ipaddr)
    {
        this.ipaddr = ipaddr;
    }
 
    public String getLoginLocation()
    {
        return loginLocation;
    }
 
    public void setLoginLocation(String loginLocation)
    {
        this.loginLocation = loginLocation;
    }
 
    public String getBrowser()
    {
        return browser;
    }
 
    public void setBrowser(String browser)
    {
        this.browser = browser;
    }
 
    public String getOs()
    {
        return os;
    }
 
    public void setOs(String os)
    {
        this.os = os;
    }
 
    public Long getExpireTime()
    {
        return expireTime;
    }
 
    public void setExpireTime(Long expireTime)
    {
        this.expireTime = expireTime;
    }
 
    public Set<String> getPermissions()
    {
        return permissions;
    }
 
    public void setPermissions(Set<String> permissions)
    {
        this.permissions = permissions;
    }
 
    public Object getUser()
    {
        return user;
    }
 
    public void setUser(Object user)
    {
        this.user = user;
    }
 
    public Class getUserClass() {
        return userClass;
    }
 
    public void setUserClass(Class userClass) {
        this.userClass = userClass;
    }
 
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
    {
        return null;
    }
}

7、另一套用戶controller登錄認(rèn)證方法

注意這里換了security提供的AuthToken,這個(gè)token會(huì)調(diào)用security內(nèi)部的DaoAuthenticationProvider進(jìn)行認(rèn)證

        // 用戶驗(yàn)證
        Authentication authentication = null;
        try
        {
            // 該方法會(huì)去調(diào)用UserDetailsServiceImpl.loadUserByUsername
            // 該方式使用的security內(nèi)置token會(huì)使用內(nèi)置DaoAuthenticationProvider認(rèn)證
            // UserDetailsServiceImpl是在security config中配置的
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {               
                throw new UserPasswordNotMatchException();
            }
            else
            {                
                throw new CustomException(e.getMessage());
            }
        }       
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();// 該方法會(huì)去調(diào)用

8、另一套用戶service

可參照上述service寫,查詢另一張用戶表即可,返回UserDetails

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論