成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Spring Security 進(jìn)階-原理篇

android_c / 2302人閱讀

摘要:過(guò)濾器基本都是通過(guò)過(guò)濾器來(lái)完成配置的身份認(rèn)證權(quán)限認(rèn)證以及登出。密碼比對(duì)通過(guò)進(jìn)行密碼比對(duì)注可自定義通過(guò)獲取通過(guò)獲取生成身份認(rèn)證通過(guò)后最終返回的記錄認(rèn)證的身份信息

知彼知己方能百戰(zhàn)百勝,用 Spring Security 來(lái)滿(mǎn)足我們的需求最好了解其原理,這樣才能隨意拓展,本篇文章主要記錄 Spring Security 的基本運(yùn)行流程。
過(guò)濾器

Spring Security 基本都是通過(guò)過(guò)濾器來(lái)完成配置的身份認(rèn)證、權(quán)限認(rèn)證以及登出。

Spring Security 在 Servlet 的過(guò)濾鏈(filter chain)中注冊(cè)了一個(gè)過(guò)濾器 FilterChainProxy,它會(huì)把請(qǐng)求代理到 Spring Security 自己維護(hù)的多個(gè)過(guò)濾鏈,每個(gè)過(guò)濾鏈會(huì)匹配一些 URL,如果匹配則執(zhí)行對(duì)應(yīng)的過(guò)濾器。過(guò)濾鏈?zhǔn)怯许樞虻?,一個(gè)請(qǐng)求只會(huì)執(zhí)行第一條匹配的過(guò)濾鏈。Spring Security 的配置本質(zhì)上就是新增、刪除、修改過(guò)濾器。

默認(rèn)情況下系統(tǒng)幫我們注入的這 15 個(gè)過(guò)濾器,分別對(duì)應(yīng)配置不同的需求。接下來(lái)我們重點(diǎn)是分析下 UsernamePasswordAuthenticationFilter 這個(gè)過(guò)濾器,他是用來(lái)使用用戶(hù)名和密碼登錄認(rèn)證的過(guò)濾器,但是很多情況下我們的登錄不止是簡(jiǎn)單的用戶(hù)名和密碼,又可能是用到第三方授權(quán)登錄,這個(gè)時(shí)候我們就需要使用自定義過(guò)濾器,當(dāng)然這里不做詳細(xì)說(shuō)明,只是說(shuō)下自定義過(guò)濾器怎么注入。

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    http.addFilterAfter(...);
    ...
 }
身份認(rèn)證流程

在開(kāi)始身份認(rèn)證流程之前我們需要了解下幾個(gè)基本概念

1.SecurityContextHolder

SecurityContextHolder 存儲(chǔ) SecurityContext 對(duì)象。SecurityContextHolder 是一個(gè)存儲(chǔ)代理,有三種存儲(chǔ)模式分別是:

MODE_THREADLOCAL:SecurityContext 存儲(chǔ)在線(xiàn)程中。

MODE_INHERITABLETHREADLOCAL:SecurityContext 存儲(chǔ)在線(xiàn)程中,但子線(xiàn)程可以獲取到父線(xiàn)程中的 SecurityContext。

MODE_GLOBAL:SecurityContext 在所有線(xiàn)程中都相同。

SecurityContextHolder 默認(rèn)使用 MODE_THREADLOCAL 模式,SecurityContext 存儲(chǔ)在當(dāng)前線(xiàn)程中。調(diào)用 SecurityContextHolder 時(shí)不需要顯示的參數(shù)傳遞,在當(dāng)前線(xiàn)程中可以直接獲取到 SecurityContextHolder 對(duì)象。

//獲取當(dāng)前線(xiàn)程里面認(rèn)證的對(duì)象
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();

//保存認(rèn)證對(duì)象 (一般用于自定義認(rèn)證成功保存認(rèn)證對(duì)象)
SecurityContextHolder.getContext().setAuthentication(authResult);

//清空認(rèn)證對(duì)象 (一般用于自定義登出清空認(rèn)證對(duì)象)
SecurityContextHolder.clearContext();
2.Authentication

Authentication 即驗(yàn)證,表明當(dāng)前用戶(hù)是誰(shuí)。什么是驗(yàn)證,比如一組用戶(hù)名和密碼就是驗(yàn)證,當(dāng)然錯(cuò)誤的用戶(hù)名和密碼也是驗(yàn)證,只不過(guò) Spring Security 會(huì)校驗(yàn)失敗。

Authentication 接口

public interface Authentication extends Principal, Serializable {
       //獲取用戶(hù)權(quán)限,一般情況下獲取到的是用戶(hù)的角色信息
       Collection getAuthorities();
       //獲取證明用戶(hù)認(rèn)證的信息,通常情況下獲取到的是密碼等信息,不過(guò)登錄成功就會(huì)被移除
       Object getCredentials();
       //獲取用戶(hù)的額外信息,比如 IP 地址、經(jīng)緯度等
       Object getDetails();
       //獲取用戶(hù)身份信息,在未認(rèn)證的情況下獲取到的是用戶(hù)名,在已認(rèn)證的情況下獲取到的是 UserDetails (暫時(shí)理解為,當(dāng)前應(yīng)用用戶(hù)對(duì)象的擴(kuò)展)
       Object getPrincipal();
       //獲取當(dāng)前 Authentication 是否已認(rèn)證
       boolean isAuthenticated();
       //設(shè)置當(dāng)前 Authentication 是否已認(rèn)證
       void setAuthenticated(boolean isAuthenticated);
}
3.AuthenticationManager ProviderManager AuthenticationProvider

其實(shí)這三者很好區(qū)分,AuthenticationManager 主要就是為了完成身份認(rèn)證流程,ProviderManagerAuthenticationManager 接口的具體實(shí)現(xiàn)類(lèi),ProviderManager 里面有個(gè)記錄 AuthenticationProvider 對(duì)象的集合屬性 providersAuthenticationProvider 接口類(lèi)里有兩個(gè)方法

public interface AuthenticationProvider {
    //實(shí)現(xiàn)具體的身份認(rèn)證邏輯,認(rèn)證失敗拋出對(duì)應(yīng)的異常
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
    //該認(rèn)證類(lèi)是否支持該 Authentication 的認(rèn)證
    boolean supports(Class authentication);
}

接下來(lái)就是遍歷 ProviderManager 里面的 providers 集合,找到和合適的 AuthenticationProvider 完成身份認(rèn)證。

4.UserDetailsService UserDetails

UserDetailsService 接口中只有一個(gè)簡(jiǎn)單的方法

public interface UserDetailsService {
    //根據(jù)用戶(hù)名查到對(duì)應(yīng)的 UserDetails 對(duì)象
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
5.流程

對(duì)于上面概念有什么不明白的地方,在們?cè)诮酉聛?lái)的流程中慢慢分析

在運(yùn)行到 UsernamePasswordAuthenticationFilter 過(guò)濾器的時(shí)候首先是進(jìn)入其父類(lèi) AbstractAuthenticationProcessingFilterdoFilter() 方法中

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
    ...
    //首先配對(duì)是不是配置的身份認(rèn)證的URI,是則執(zhí)行下面的認(rèn)證,不是則跳過(guò)
    if (!requiresAuthentication(request, response)) {
        chain.doFilter(request, response);

        return;
    }
    ...
    Authentication authResult;

    try {
        //關(guān)鍵方法, 實(shí)現(xiàn)認(rèn)證邏輯并返回 Authentication, 由其子類(lèi) UsernamePasswordAuthenticationFilter 實(shí)現(xiàn), 由下面 5.3 詳解
        authResult = attemptAuthentication(request, response);
        if (authResult == null) {
            // return immediately as subclass has indicated that it hasn"t completed
            // authentication
            return;
        }
        sessionStrategy.onAuthentication(authResult, request, response);
    }
    catch (InternalAuthenticationServiceException failed) {
        //認(rèn)證失敗調(diào)用...由下面 5.1 詳解
        unsuccessfulAuthentication(request, response, failed);

        return;
    }
    catch (AuthenticationException failed) {
        //認(rèn)證失敗調(diào)用...由下面 5.1 詳解
        unsuccessfulAuthentication(request, response, failed);

        return;
    }

    // Authentication success
    if (continueChainBeforeSuccessfulAuthentication) {
        chain.doFilter(request, response);
    }
    //認(rèn)證成功調(diào)用...由下面 5.2 詳解
    successfulAuthentication(request, response, chain, authResult);
}
5.1 認(rèn)證失敗處理邏輯
protected void unsuccessfulAuthentication(HttpServletRequest request,
                                          HttpServletResponse response, AuthenticationException failed)
        throws IOException, ServletException {
    SecurityContextHolder.clearContext();
    ...
    rememberMeServices.loginFail(request, response);
    //該 handler 處理失敗界面跳轉(zhuǎn)和響應(yīng)邏輯
    failureHandler.onAuthenticationFailure(request, response, failed);
}

這里默認(rèn)配置的失敗處理 handler 是 SimpleUrlAuthenticationFailureHandler,可自定義。

public class SimpleUrlAuthenticationFailureHandler implements
        AuthenticationFailureHandler {
    ...

    public void onAuthenticationFailure(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException exception)
            throws IOException, ServletException {
        //沒(méi)有配置失敗跳轉(zhuǎn)的URL則直接響應(yīng)錯(cuò)誤
        if (defaultFailureUrl == null) {
            logger.debug("No failure URL set, sending 401 Unauthorized error");

            response.sendError(HttpStatus.UNAUTHORIZED.value(),
                HttpStatus.UNAUTHORIZED.getReasonPhrase());
        }
        else {
            //否則
            //緩存異常
            saveException(request, exception);
            //根據(jù)配置的異常頁(yè)面是重定向還是轉(zhuǎn)發(fā)進(jìn)行不同方式跳轉(zhuǎn)
            if (forwardToDestination) {
                logger.debug("Forwarding to " + defaultFailureUrl);

                request.getRequestDispatcher(defaultFailureUrl)
                        .forward(request, response);
            }
            else {
                logger.debug("Redirecting to " + defaultFailureUrl);
                redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
            }
        }
    }
    //緩存異常,轉(zhuǎn)發(fā)則保存在request里面,重定向則保存在session里面
    protected final void saveException(HttpServletRequest request,
            AuthenticationException exception) {
        if (forwardToDestination) {
            request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
        }
        else {
            HttpSession session = request.getSession(false);

            if (session != null || allowSessionCreation) {
                request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,
                        exception);
            }
        }
    }
}

這里做下小拓展:用系統(tǒng)的錯(cuò)誤處理handler,指定認(rèn)證失敗跳轉(zhuǎn)的URL,在MVC里面對(duì)應(yīng)的URL方法里面可以通過(guò)key從requestsession里面拿到錯(cuò)誤信息,反饋給前端

5.2 認(rèn)證成功處理邏輯
protected void successfulAuthentication(HttpServletRequest request,
                                        HttpServletResponse response, FilterChain chain, Authentication authResult)
        throws IOException, ServletException {
    ...
    //這里要注意很重要,將認(rèn)證完成返回的 Authentication 保存到線(xiàn)程對(duì)應(yīng)的 `SecurityContext` 中
    SecurityContextHolder.getContext().setAuthentication(authResult);

    rememberMeServices.loginSuccess(request, response, authResult);

    // Fire event
    if (this.eventPublisher != null) {
        eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
                authResult, this.getClass()));
    }
    //該 handler 就是為了完成頁(yè)面跳轉(zhuǎn)
    successHandler.onAuthenticationSuccess(request, response, authResult);
}

這里默認(rèn)配置的成功處理 handler 是 SavedRequestAwareAuthenticationSuccessHandler,里面的代碼就不做具體展開(kāi)了,反正是跳轉(zhuǎn)到指定的認(rèn)證成功之后的界面,可自定義

5.3 身份認(rèn)證詳情
public class UsernamePasswordAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {
    ...
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private boolean postOnly = true;

    ...
    //開(kāi)始身份認(rèn)證邏輯
    public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();
        //先用前端提交過(guò)來(lái)的 username 和 password 封裝一個(gè)簡(jiǎn)易的 AuthenticationToken
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        //具體的認(rèn)證邏輯還是交給 AuthenticationManager 對(duì)象的 authenticate(..) 方法完成,接著往下看
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

由源碼斷點(diǎn)跟蹤得知,最終解析是由 AuthenticationManager 接口實(shí)現(xiàn)類(lèi) ProviderManager 來(lái)完成

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
        InitializingBean {
    ...
    private List providers = Collections.emptyList();
    ...
    
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        ....
        //遍歷所有的 AuthenticationProvider, 找到合適的完成身份驗(yàn)證
        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }
            ...
            try {
                //進(jìn)行具體的身份驗(yàn)證邏輯, 這里使用到的是 DaoAuthenticationProvider, 具體邏輯記著往下看
                result = provider.authenticate(authentication);

                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            }
            catch 
            ...
        }
        ...
        throw lastException;
    }
}

DaoAuthenticationProvider 繼承自 AbstractUserDetailsAuthenticationProvider 實(shí)現(xiàn)了 AuthenticationProvider 接口

public abstract class AbstractUserDetailsAuthenticationProvider implements
        AuthenticationProvider, InitializingBean, MessageSourceAware {
    ...
    private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();
    private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();
    ...

    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        ...
        // 獲得提交過(guò)來(lái)的用戶(hù)名
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();
        //根據(jù)用戶(hù)名從緩存中查找 UserDetails
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);

        if (user == null) {
            cacheWasUsed = false;

            try {
                //緩存中沒(méi)有則通過(guò) retrieveUser(..) 方法查找 (看下面 DaoAuthenticationProvider 的實(shí)現(xiàn))
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            catch 
            ...
        }

        try {
            //比對(duì)前的檢查,例如賬戶(hù)以一些狀態(tài)信息(是否鎖定, 過(guò)期...)
            preAuthenticationChecks.check(user);
            //子類(lèi)實(shí)現(xiàn)比對(duì)規(guī)則 (看下面 DaoAuthenticationProvider 的實(shí)現(xiàn))
            additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
        }
        catch (AuthenticationException exception) {
            if (cacheWasUsed) {
                // There was a problem, so try again after checking
                // we"re using latest data (i.e. not from the cache)
                cacheWasUsed = false;
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
                preAuthenticationChecks.check(user);
                additionalAuthenticationChecks(user,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            else {
                throw exception;
            }
        }

        postAuthenticationChecks.check(user);

        if (!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }

        Object principalToReturn = user;

        if (forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }
        //根據(jù)最終user的一些信息重新生成具體詳細(xì)的 Authentication 對(duì)象并返回 
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }
    //具體生成還是看子類(lèi)實(shí)現(xiàn)
    protected Authentication createSuccessAuthentication(Object principal,
            Authentication authentication, UserDetails user) {
        // Ensure we return the original credentials the user supplied,
        // so subsequent attempts are successful even with encoded passwords.
        // Also ensure we return the original getDetails(), so that future
        // authentication events after cache expiry contain the details
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
                principal, authentication.getCredentials(),
                authoritiesMapper.mapAuthorities(user.getAuthorities()));
        result.setDetails(authentication.getDetails());

        return result;
    }
}

接下來(lái)我們來(lái)看下 DaoAuthenticationProvider 里面的三個(gè)重要的方法,比對(duì)方式、獲取需要比對(duì)的 UserDetails 對(duì)象以及生產(chǎn)最終返回 Authentication 的方法。

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    ...
    //密碼比對(duì)
    @SuppressWarnings("deprecation")
    protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }

        String presentedPassword = authentication.getCredentials().toString();
        //通過(guò) PasswordEncoder 進(jìn)行密碼比對(duì), 注: 可自定義
        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            logger.debug("Authentication failed: password does not match stored value");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }

    //通過(guò) UserDetailsService 獲取 UserDetails
    protected final UserDetails retrieveUser(String username,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        prepareTimingAttackProtection();
        try {
            //通過(guò) UserDetailsService 獲取 UserDetails
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException(
                        "UserDetailsService returned null, which is an interface contract violation");
            }
            return loadedUser;
        }
        catch (UsernameNotFoundException ex) {
            mitigateAgainstTimingAttack(authentication);
            throw ex;
        }
        catch (InternalAuthenticationServiceException ex) {
            throw ex;
        }
        catch (Exception ex) {
            throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
        }
    }

    //生成身份認(rèn)證通過(guò)后最終返回的 Authentication, 記錄認(rèn)證的身份信息
    @Override
    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);
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73857.html

相關(guān)文章

  • Spring Security 進(jìn)階-加密

    摘要:在中加密是一個(gè)很簡(jiǎn)單卻又不能忽略的模塊,數(shù)據(jù)只有加密起來(lái)才更安全,這樣就散算據(jù)庫(kù)密碼泄漏也都是密文。當(dāng)然也可以自定義構(gòu)造方法,來(lái)制定用其他的方案進(jìn)行加密。應(yīng)用先示范下使用系統(tǒng)的來(lái)演示下簡(jiǎn)單的注入構(gòu)造加密方案 在 Spring Security 中加密是一個(gè)很簡(jiǎn)單卻又不能忽略的模塊,數(shù)據(jù)只有加密起來(lái)才更安全,這樣就散算據(jù)庫(kù)密碼泄漏也都是密文。本文分析對(duì)應(yīng)的版本是 5.14。 概念 Spr...

    wanglu1209 評(píng)論0 收藏0
  • Spring Security 進(jìn)階-細(xì)節(jié)總結(jié)

    摘要:但是我們最好不要在里面對(duì)他進(jìn)行處理,而是放到配置的權(quán)限異常來(lái)處理。記得配置登錄認(rèn)證前和過(guò)程中的一些請(qǐng)求不需要身份認(rèn)證。登錄認(rèn)證失敗不能直接拋出錯(cuò)誤,需要向前端響應(yīng)異常。 關(guān)于 Spring Security 的學(xué)習(xí)已經(jīng)告一段落了,剛開(kāi)始接觸該安全框架感覺(jué)很迷茫,總覺(jué)得沒(méi)有 Shiro 靈活,到后來(lái)的深入學(xué)習(xí)和探究才發(fā)現(xiàn)它非常強(qiáng)大。簡(jiǎn)單快速集成,基本不用寫(xiě)任何代碼,拓展起來(lái)也非常靈活和強(qiáng)...

    LinkedME2016 評(píng)論0 收藏0
  • 你和阿里資深架構(gòu)師之間,差的不僅僅是年齡(進(jìn)階必看)

    摘要:導(dǎo)讀閱讀本文需要有足夠的時(shí)間,筆者會(huì)由淺到深帶你一步一步了解一個(gè)資深架構(gòu)師所要掌握的各類(lèi)知識(shí)點(diǎn),你也可以按照文章中所列的知識(shí)體系對(duì)比自身,對(duì)自己進(jìn)行查漏補(bǔ)缺,覺(jué)得本文對(duì)你有幫助的話(huà),可以點(diǎn)贊關(guān)注一下。目錄一基礎(chǔ)篇二進(jìn)階篇三高級(jí)篇四架構(gòu)篇五擴(kuò) 導(dǎo)讀:閱讀本文需要有足夠的時(shí)間,筆者會(huì)由淺到深帶你一步一步了解一個(gè)資深架構(gòu)師所要掌握的各類(lèi)知識(shí)點(diǎn),你也可以按照文章中所列的知識(shí)體系對(duì)比自身,對(duì)自己...

    huaixiaoz 評(píng)論0 收藏0
  • Java學(xué)習(xí)路線(xiàn)總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強(qiáng))

    摘要:哪吒社區(qū)技能樹(shù)打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁(yè)左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無(wú)意間聽(tīng)到領(lǐng)導(dǎo)們的談話(huà),現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹(shù)打卡?【打卡貼 day2...

    Scorpion 評(píng)論0 收藏0
  • Spring Security

    摘要:框架具有輕便,開(kāi)源的優(yōu)點(diǎn),所以本譯見(jiàn)構(gòu)建用戶(hù)管理微服務(wù)五使用令牌和來(lái)實(shí)現(xiàn)身份驗(yàn)證往期譯見(jiàn)系列文章在賬號(hào)分享中持續(xù)連載,敬請(qǐng)查看在往期譯見(jiàn)系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪(fǎng)問(wèn)層和前端控制器但是忽略了對(duì)身份進(jìn)行驗(yàn)證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<