日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Security前后端分離自定義登錄詳解

作者:為了我的架構師 更新時間: 2022-04-17 編程語言

Security前后端分離自定義登錄

思路:

通過自定義接口去處理前端的登錄請求,其主要問題就是如何實現登錄的認證!我們需要手動調用security的認證,并且在security配置類中需要定義一些東西

解決:

security配置類(我這里只是測試,根據個人使用更改即可):

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 查詢用戶信息
        provider.setUserDetailsService(userDetailsService());
        // 設置密碼加密算法
        provider.setPasswordEncoder(passwordEncoder());
        auth.authenticationProvider(provider);
    }

    @Bean
    @Override
    //構建用戶(可以從數據庫查找、內存模擬,這里為了方便就使用內存構建)
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("123456").authorities("p1").build());
        manager.createUser(User.withUsername("lisi").password("123456").authorities("p2").build());
        return manager;
    }
    @Bean
    //生成密碼編碼類,像MD5,這里使用的這個是直接比較String
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    //security配置,核心
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                //訪問/test/1,需要p1權限
                .antMatchers("/test/1").hasAnyAuthority("p1")
                //訪問/test/2,需要p2權限
                .antMatchers("/test/2").hasAnyAuthority("p2")
                //test下的請求都必須認證(登錄)后才能訪問
                .antMatchers("/test/**").authenticated()
                //無條件放行test/** 以外的請求
                .anyRequest().permitAll();
        
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}

測試接口:

@RestController
public class TestController {
    @Resource
    AuthenticationManager authenticationManager;
    
    @GetMapping("/mylogin")
    public String test(String username, String password){
        //生成驗證令牌
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,password);
        //獲取驗證完成的認證信息
        Authentication authenticate = authenticationManager.authenticate(token);
        //保存到security上下文中
        SecurityContextHolder.getContext().setAuthentication(authenticate);
        return "登錄成功";
    }

    @GetMapping("/test/1")
    public String test1(){
        return "有p1權限,認證后才能看到這條消息";
    }

    @GetMapping("/test/2")
    public String test2(){
        return "有p2權限,認證后才能看到這條消息";
    }
}

詳解:

1.配置類中此方法

image-20211114213750121

為什么需要使用DaoAuthenticationProvider,使用為我們使用用戶名密碼是使用UsernamePasswordAuthenticationToken生成的驗證令牌(登錄接口中也是這樣寫的),而UsernamePasswordAuthenticationToken類型的驗證令牌最終會匹配到DaoAuthenticationProvider來進行驗證(因為只有他能驗證這種類型的)。

我們需要給這個驗證提供者(DaoAuthenticationProvider)提供用戶信息和密碼使用什么加密算法。

提供用戶信息即圖中:setUserDetailsService(),其中的userDetailsService()方法是你在配置類中定義的,里面你可以自定定義邏輯,比如最常用的數據庫查詢用戶信息

加密算法即圖中:setPasswordEncoder(),其中的passwordEncoder()方法也是在配置類中定義的,稍后會講解那一塊

2.配置類的userDetailsService()方法

image-20211114220730298

這里是在內存中創建的數據,要查數據庫怎么玩??(見名知意它是個service,service掉dao就能拿到用戶信息了)

@Override
    @Bean
    protected UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl();
    }

你需要寫一個類實現UserDetailsService,實現其方法loadUserByUsername(String username)即可,在這個方法里面你就可以調用dao去查這個用戶,并將其返回(返回類型是userdetails),所以用戶實體類必須繼承User類:如下介紹

是Security中的User類

import org.springframework.security.core.userdetails.User;

這個security中的User,該User實現了UserDetails,所以我們繼承即可。繼承之后我們只需要創建兩個

構造函數即可,它有錯誤提示,點一下就幫我們創建好了:

public class MyUser extends User {
    private String userName;
    private String password;

    public MyUser(String username, String password, Collection authorities) {
        super(username, password, authorities);
    }

    public MyUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    }
}

3. 密碼加密方式

image-20211114224838212

其作用是選擇一個密碼加密方式,就是在做密碼比對的時候,用什么加密方式去加密前端傳來的密碼,然后再和數據庫中的密碼比較!

4.注入AuthenticionManager

image-20211114225130043

就是注入一個認證管理器,非要說些什么的話,就說說它是個什么吧。

AuthenticionManager是一個接口,看它的意思就知道它是管理認證的,該接口中也只有一個認證方法:image-20211114225637875

看看它的實現類image-20211114225713726

其實現類ProviderManager(它是做認證的,delegator結尾的是授權的。我也不敢確定說對沒有。。)就是我們用到的,它會幫我們找到能夠驗證的認證提供類(即DaoAuthenticionProvider)

5.我的登錄接口為啥用的get請求方式

不知道你們注意到我的登錄接口是get請求沒有,是這樣的:

因為我并沒有寫前端,我之前使用的post方式,我圖方便就在postman上測的,能夠登錄成功也確實認證成功,但是,并不能訪問需要身份認證后才能訪問的請求,我明明已經認證成功了呀?

是因為,認證成功之后,你的認證信息會被放到session中

image-20211114231719283

如圖,這是security初始化內部實例時(springboot啟動的時候),使用的就是httpsession,你要問在哪用到了這個repo:

image-20211114231921030

我們只需要在接口中,將認證信息保存到security上下文即可,后續就交給security處理:

image-20211114232822236

所以,你必須在同一個瀏覽器中先登錄認證才能有這個session,也就才能訪問得到你有權訪問的請求(當然你也可以在postman中加上這個session,不過就麻煩很多,我使用get效果一樣的)

image-20211114233234091

最后這里有一篇不錯的關于security前后端分離登錄的文章:https://blog.csdn.net/ycf921244819/article/details/108538506

這里是我分析的security認證過程源碼(有點亂,但最后兩張圖比較清晰),有興趣的可以來看看:

https://blog.csdn.net/qq_42682745/article/details/120713818

原文鏈接:https://blog.csdn.net/qq_42682745/article/details/121326021

欄目分類
最近更新