最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringSecurity實(shí)現(xiàn)前后端分離登錄token認(rèn)證詳解

 更新時(shí)間:2023年06月13日 09:10:36   作者:山河亦問安  
目前市面上比較流行的權(quán)限框架主要實(shí)Shiro和Spring Security,這兩個(gè)框架各自側(cè)重點(diǎn)不同,各有各的優(yōu)劣,本文將給大家詳細(xì)介紹SpringSecurity如何實(shí)現(xiàn)前后端分離登錄token認(rèn)證

1. SpringSecurity概述

1.1 權(quán)限框架

目前市面上比較流行的權(quán)限框架主要實(shí)Shiro和Spring Security,這兩個(gè)框架各自側(cè)重點(diǎn)不同,各有各的優(yōu)劣。

1.1.1 Apache Shiro

Apache Shiro(讀作"sheeroh”,即日語"城")是一個(gè)開源安全框架,提供身份驗(yàn)證、授權(quán)、密碼學(xué)和會(huì)話管理。Shiro框架直觀、易用,同時(shí)也能提供健壯的安全性。

特點(diǎn):

Shiro的特點(diǎn):

1. 易于理解的Java Security APl;
2. 簡單的身份認(rèn)證(登錄),支持多種數(shù)據(jù)源(LDAP,JDBC,Kerberos,ActiveDirectory等);·
3. 對角色的簡單的簽權(quán)(訪問控制),支持細(xì)粒度的簽權(quán);
4. 支持一級緩存,以提升應(yīng)用程序的性能;
5. 內(nèi)置的基于POJO企業(yè)會(huì)話管理,適用于Web 以及非 Web的環(huán)境;
6.異構(gòu)客戶端會(huì)話訪問;
7.非常簡單的加密API;
8.不跟任何的框架或者容器捆綁,可以獨(dú)立運(yùn)行。

1.1.2 SpringSecurity

Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供描述性安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC(依賴注入,也稱控制反轉(zhuǎn))和AOP(面向切面編程)功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,減少了為企業(yè)系統(tǒng)安全控制編寫大量重復(fù)代碼的工作。

Spring Security是Spring家族中的一個(gè)安全管理框架。相比與另外一個(gè)安全框架Shiro,它提供了更豐富的功能,社區(qū)資源也比shiro豐富。一般Web應(yīng)用的需要進(jìn)行認(rèn)證和授權(quán)。而認(rèn)證和授權(quán)也是SpringSecurity作為安全框架的核心功能。

SpringSecurity的特點(diǎn):

1. 與Spring Boot集成非常簡單。
2. 功能強(qiáng)大,高度可定制化。
3. 支持OAuth2.0。
4. 強(qiáng)大的加密ARI。
5. 防止跨站請求偽造攻擊(CSRF)。
6. 提供Spring Cloud分布式組件。

1.1.3 權(quán)限框架的選擇

Spring Security 是 Spring 家族中的一個(gè)安全管理框架,實(shí)際上,在 Spring Boot 出現(xiàn)之前,Spring Security 就已經(jīng)發(fā)展了多年了,但是使用的并不多,安全管理這個(gè)領(lǐng)域,一直是 Shiro 的天下。
相對于 Shiro,在 SSM 中整合 Spring Security 都是比較麻煩的操作,所以,SpringSecurity 雖然功能比 Shiro 強(qiáng)大,但是使用反而沒有 Shiro 多(Shiro 雖然功能沒有Spring Security 多,但是對于大部分項(xiàng)目而言,Shiro 也夠用了)。自從有了 Spring Boot 之后,Spring Boot 對于 Spring Security 提供了自動(dòng)化配置方案,可以使用更少的配置來使用 Spring Security。

1.2 授權(quán)和認(rèn)證

一般來說,Web應(yīng)用的安全性包括用戶認(rèn)證(Authentication)和用戶授權(quán)(Authorization)兩個(gè)部分,這兩點(diǎn)也是SpringSecurity重要核心功能。

1)用戶認(rèn)證:驗(yàn)證某個(gè)用戶是否為系統(tǒng)中的合法主體,也就是說用戶能否訪問該系統(tǒng)。用戶認(rèn)證一般要求用戶提供用戶名和密碼。系統(tǒng)通過校驗(yàn)用戶名和密碼來完成認(rèn)證過程。

(2)用戶授權(quán):是驗(yàn)證某個(gè)用戶是否有權(quán)限執(zhí)行某個(gè)操作。經(jīng)過認(rèn)證后判斷當(dāng)前用戶是否有權(quán)限進(jìn)行某個(gè)操作。

下面進(jìn)行介紹一下RBAC:

RBAC (Role Based Access Control)基于角色的訪問控制,通過抽象出“用戶、角色、權(quán)限”"三個(gè)概念,實(shí)現(xiàn)用戶分配角色,角色分配權(quán)限的權(quán)限管理方式,也是目前企業(yè)中權(quán)限管理主要實(shí)現(xiàn)方案。

案例如下圖所示:

1.3 SpringSecurity的功能

Spring Security對Web安全性的支持大量地依賴于Servlet過濾器。這些過濾器攔截進(jìn)入請求,并且在應(yīng)用程序處理該請求之前進(jìn)行某些安全處理。 Spring Security提供有若干個(gè)過濾器,它們能夠攔截Servlet請求,并將這些請求轉(zhuǎn)給認(rèn)證和訪問決策管理器處理,從而增強(qiáng)安全性。

如今的Spring Security已經(jīng)成為Spring Framework下最成熟的安全系統(tǒng),它為我們提供了強(qiáng)大而靈活的企業(yè)級安全服務(wù),如:

Ø 認(rèn)證授權(quán)機(jī)制

Ø Web資源訪問控制

Ø 業(yè)務(wù)方法調(diào)用訪問控制

Ø 領(lǐng)域?qū)ο笤L問控制Access Control List(ACL)

Ø 單點(diǎn)登錄(Central Authentication Service)

Ø 信道安全(Channel Security)管理等功能

2.SpringSecurity 實(shí)戰(zhàn)

2.1 引入SpringSecurity

導(dǎo)入依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

引入依賴后我們在嘗試去訪問之前的接口就會(huì)自動(dòng)跳轉(zhuǎn)到一個(gè)SpringSecurity的默認(rèn)登陸頁面,默認(rèn)用戶名是user,密碼會(huì)輸出在控制臺。必須登陸之后才能對接口進(jìn)行訪問。

2.2 認(rèn)證

2.2.1 登錄校驗(yàn)流程

登錄校驗(yàn)流程如下圖所示:

2.2.2 SpringSecurity完整流程

SpringSecurity的原理其實(shí)就是一個(gè)過濾器鏈,內(nèi)部包含了提供各種功能的過濾器。這里我們可以看看入門案例中的過濾器。

UsernamePasswordAuthenticationFilter:負(fù)責(zé)處理我們在登陸頁面填寫了用戶名密碼后的登陸請求。入門案例的認(rèn)證工作主要有它負(fù)責(zé)。

ExceptionTranslationFilter:處理過濾器鏈中拋出的任何AccessDeniedException和AuthenticationException。

FilterSecurityInterceptor:負(fù)責(zé)權(quán)限校驗(yàn)的過濾器。

我們可以通過Debug查看當(dāng)前系統(tǒng)中SpringSecurity過濾器鏈中有哪些過濾器及它們的順序。如下圖所示:

2.2.3 認(rèn)證流程詳解

Authentication接口::它的實(shí)現(xiàn)類,表示當(dāng)前訪問系統(tǒng)的用戶,封裝了用戶相關(guān)信息。

AuthenticationManager接口:定義了認(rèn)證Authentication的方法。

UserDetailsService接口:加載用戶特定數(shù)據(jù)的核心接口。里面定義了一個(gè)根據(jù)用戶名查詢用戶信息的方法。

UserDetails接口:提供核心用戶信息。通過UserDetailsService根據(jù)用戶名獲取處理的用戶信息要封裝成UserDetails對象返回。然后將這些信息封裝到Authentication對象中。

2.3 思路分析

在前后端分離中我們一般采用token驗(yàn)證,所以在這里我們要自定義登錄接口,通過自定義接口調(diào)用調(diào)用ProviderManager的方法進(jìn)行認(rèn)證 ,如果認(rèn)證通過生成jwt,然后將jwt返回給前端,同時(shí)將用戶信息包括用戶權(quán)限信息等存入到redis數(shù)據(jù)庫中,redis數(shù)據(jù)庫作為緩存讀取速度遠(yuǎn)遠(yuǎn)大于從數(shù)據(jù)庫中進(jìn)行讀取用戶信息。我們還要自定義UserDetailsService,在這個(gè)實(shí)現(xiàn)類中去查詢數(shù)據(jù)庫。除此之外我們還需要定義JWT認(rèn)證過濾器,從前端請求頭中獲取token,解析token獲取其中的userid,然后根據(jù)userid從redis中獲取用戶信息存入SecurityContextHolder。具體流程如下圖:

2.4 代碼實(shí)戰(zhàn)

2.4.1  自定義UserDetailsService類

首先定義一個(gè)UserDetails類代碼如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails, Serializable {
    private User user;
    //返回權(quán)限信息
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    //返回用戶密碼
    @Override
    public String getPassword() {
        return user.getPassword();
    }
       //返回用戶賬號
    @Override
    public String getUsername() {
        return user.getLoginname();
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

自定義實(shí)現(xiàn)UserDetailsService接口,重寫其中的方法。代碼如下:

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    TbUserMapper tbUserMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根據(jù)用戶名從數(shù)據(jù)庫中查詢數(shù)據(jù)信息
        QueryWrapper<TbUser> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("username",username);
        TbUser tbUser = tbUserMapper.selectOne(queryWrapper);
        //查詢不到該用戶信息拋異常
        if(tbUser==null){
            throw new RuntimeException("用戶名或者密碼錯(cuò)誤");
        }
       //封裝成UserDetails返回
        return new MyUser(tbUser);
    }
}

2.4.2 密碼加密存儲

實(shí)際項(xiàng)目中我們不會(huì)把密碼明文存儲在數(shù)據(jù)庫中。 
?1. 默認(rèn)使用的PasswordEncoder要求數(shù)據(jù)庫中的密碼格式為:{id}password 。它會(huì)根據(jù)id去判斷密碼的加密方式。但是我們一般不會(huì)采用這種方式。所以就需要替換PasswordEncoder。?

2.我們一般使用SpringSecurity為我們提供的BCryptPasswordEncoder。

3.我們只需要使用把BCryptPasswordEncoder對象注入Spring容器中,SpringSecurity就會(huì)使用該P(yáng)asswordEncoder來進(jìn)行密碼校驗(yàn)。

4.?我們可以定義一個(gè)SpringSecurity的配置類,SpringSecurity要求這個(gè)配置類要繼承WebSecurityConfigurerAdapter。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

2.4.3 登錄接口

我們需要自定義登陸接口,然后讓SpringSecurity對這個(gè)接口放行,讓用戶訪問這個(gè)接口的時(shí)候不用登錄也能訪問。 
? 在接口中我們通過AuthenticationManager的authenticate方法來進(jìn)行用戶認(rèn)證,所以需要在SecurityConfig中配置把AuthenticationManager注入容器。 
? 認(rèn)證成功的話要生成一個(gè)jwt,放入響應(yīng)中返回。并且為了讓用戶下回請求時(shí)能通過jwt識別出具體的是哪個(gè)用戶,我們需要把用戶信息存入redis有效時(shí)間為30分鐘,可以把用戶id作為key。登錄接口代碼如下:

Configuration代碼如下:

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()//關(guān)閉csrf
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不通過session獲取SecurityContext
                .and()
                .authorizeRequests()
                .antMatchers("/user/login").anonymous() //對于登錄接口允許匿名訪問
                .anyRequest().authenticated(); //除上面外的所有請求全部需要鑒權(quán)認(rèn)證
UsernamePasswordAuthenticationFilter.class);
    }
}

登錄接口代碼如下:

 
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    AuthenticationManager authenticationManager;
   @PostMapping("/user/login")
    public Result login(@RequestBody TbUser tbUser){
        //通過AuthenticationManager的authenticate方法來進(jìn)行用戶認(rèn)證
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(tbUser.getUsername(),tbUser.getPassword());
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        if(authenticate==null){
            return Result.error("401","登錄校驗(yàn)失敗");
        }
        else {
            //獲取用戶信息
            MyUser myUser = (MyUser) authenticate.getPrincipal();
            //獲取用戶id
            Long id = myUser.getTbUser().getId();
            //根據(jù)用戶id生成token
            String token = JwtUtil.generateToken(id);
            //將token放在數(shù)據(jù)庫中
            redisTemplate.opsForValue().set(String.valueOf(id),myUser,30, TimeUnit.MINUTES);
            return Result.success(token);
        }
    }

2.4.4 token認(rèn)證過濾器

需要自定義一個(gè)過濾器,這個(gè)過濾器會(huì)去獲取請求頭中的token,對token進(jìn)行解析取出其中的userid。使用userid去redis中獲取對應(yīng)的LoginUser對象。然后封裝Authentication對象存入SecurityContextHolder。代碼如下:

JwtAuthenticationTokenFilter自定義認(rèn)證過濾器代碼如下:
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    RedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        //獲取請求頭中的token
        String token = httpServletRequest.getHeader("token");
        //如果token為空直接放行,由于用戶信息沒有存放在SecurityContextHolder.getContext()中所以后面的過濾器依舊認(rèn)證失敗符合要求
        if(!StringUtils.hasText(token)){
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            return;
        }
        Long userId;
        try {
            //通過jwt工具類解析token獲得userId,如果token過期或非法就會(huì)拋異常
            DecodedJWT decodedJWT = JwtUtil.decodeToken(token);
            userId = decodedJWT.getClaim("userId").asLong();
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }
        //根據(jù)userId從redis中獲取用戶信息,如果沒有該用戶就代表該用戶沒有登錄過
        MyUser myUser = (MyUser) redisTemplate.opsForValue().get(String.valueOf(userId));
        if(Objects.isNull(myUser)){
            throw new RuntimeException("用戶未登錄");
        }
        //將用戶信息存放在SecurityContextHolder.getContext(),后面的過濾器就可以獲得用戶信息了。
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(myUser,null,null);
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }
}

在Configuration類中配置自定義過濾器,代碼如下:

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
 //自定義過濾器放在UsernamePasswordAuthenticationFilter過濾器之前
            http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

2.4.5 接口測試

在訪問/user/login接口之前,其余的接口都不能訪問到,然后先登錄訪問/user/login接口,測試結(jié)果如下圖:

然后攜帶請求頭token訪問其它方法就可以正常的進(jìn)行訪問了,由此可見我們的代碼測試驗(yàn)證成功。

至此該篇文章SpringSecurity認(rèn)證內(nèi)容結(jié)束。

以上就是SpringSecurity實(shí)現(xiàn)前后端分離登錄token認(rèn)證詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringSecurity實(shí)現(xiàn)登錄token認(rèn)證的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java并發(fā)編程Callable與Future的應(yīng)用實(shí)例代碼

    Java并發(fā)編程Callable與Future的應(yīng)用實(shí)例代碼

    這篇文章主要介紹了Java并發(fā)編程Callable與Future的應(yīng)用實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • 解決JSONField、JsonProperty不生效的問題

    解決JSONField、JsonProperty不生效的問題

    這篇文章主要介紹了解決JSONField、JsonProperty不生效的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • Java獲取本機(jī)IP地址的方法代碼示例(內(nèi)網(wǎng)、公網(wǎng))

    Java獲取本機(jī)IP地址的方法代碼示例(內(nèi)網(wǎng)、公網(wǎng))

    在IT領(lǐng)域獲取本機(jī)IP地址是一項(xiàng)基礎(chǔ)但重要的任務(wù),特別是在網(wǎng)絡(luò)編程、遠(yuǎn)程協(xié)作和設(shè)備通信中,這篇文章主要給大家介紹了關(guān)于Java獲取本機(jī)IP地址的方法(內(nèi)網(wǎng)、公網(wǎng)),需要的朋友可以參考下
    2024-07-07
  • java 讀寫文件[多種方法]

    java 讀寫文件[多種方法]

    前兩天用到讀寫文件的操作,上網(wǎng)搜了一些這方面的資料。很有用的。
    2008-11-11
  • Java如何讀取jar包中的resource資源文件

    Java如何讀取jar包中的resource資源文件

    這篇文章主要介紹了Java如何讀取jar包中的resource資源文件,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • java 三角形類 Triangle的用法詳解

    java 三角形類 Triangle的用法詳解

    這篇文章主要介紹了java 三角形類 Triangle的用法詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java源碼解析HashMap成員變量

    Java源碼解析HashMap成員變量

    今天小編就為大家分享一篇關(guān)于Java源碼解析HashMap成員變量,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Java雙括弧初始化操作技巧

    Java雙括弧初始化操作技巧

    這篇文章主要介紹了Java雙括弧初始化操作技巧,這種方法不僅提高了代碼的可讀性,而且簡化了代碼的數(shù)量,需要的朋友可以參考下
    2015-12-12
  • 你一定不知道的Java Unsafe用法詳解

    你一定不知道的Java Unsafe用法詳解

    Unsafe是位于sun.misc包下的一個(gè)類,主要提供一些用于執(zhí)行低級別、不安全操作的方法,如直接訪問系統(tǒng)內(nèi)存資源、自主管理內(nèi)存資源等,下面這篇文章主要給大家介紹了關(guān)于Java Unsafe用法的相關(guān)資料,需要的朋友可以參考下
    2021-10-10
  • mybatis查詢匹配機(jī)制圖文詳解

    mybatis查詢匹配機(jī)制圖文詳解

    這篇文章主要給大家介紹了關(guān)于mybatis查詢匹配機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評論

商水县| 阳江市| 仁化县| 即墨市| 淅川县| 唐山市| 治县。| 金川县| 锡林郭勒盟| 本溪| 泰来县| 昭觉县| 奉化市| 沙坪坝区| 北安市| 安国市| 漳州市| 永康市| 中西区| 建水县| 江陵县| 砀山县| 阜南县| 手机| 蕉岭县| 衡东县| 深水埗区| 沐川县| 祁东县| 怀宁县| 通海县| 远安县| 新晃| 资源县| 小金县| 波密县| 庆安县| 咸宁市| 乡宁县| 井冈山市| 平顶山市|