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

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

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

1. SpringSecurity概述

1.1 權(quán)限框架

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

1.1.1 Apache Shiro

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

特點:

Shiro的特點:

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

1.1.2 SpringSecurity

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

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

SpringSecurity的特點:

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

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

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

1.2 授權(quán)和認證

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

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

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

下面進行介紹一下RBAC:

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

案例如下圖所示:

1.3 SpringSecurity的功能

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

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

Ø 認證授權(quán)機制

Ø Web資源訪問控制

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

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

Ø 單點登錄(Central Authentication Service)

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

2.SpringSecurity 實戰(zhàn)

2.1 引入SpringSecurity

導入依賴

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

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

2.2 認證

2.2.1 登錄校驗流程

登錄校驗流程如下圖所示:

2.2.2 SpringSecurity完整流程

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

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

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

FilterSecurityInterceptor:負責權(quán)限校驗的過濾器。

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

2.2.3 認證流程詳解

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

AuthenticationManager接口:定義了認證Authentication的方法。

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

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

2.3 思路分析

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

2.4 代碼實戰(zhàn)

2.4.1  自定義UserDetailsService類

首先定義一個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;
    }
}

自定義實現(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("用戶名或者密碼錯誤");
        }
       //封裝成UserDetails返回
        return new MyUser(tbUser);
    }
}

2.4.2 密碼加密存儲

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

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

3.我們只需要使用把BCryptPasswordEncoder對象注入Spring容器中,SpringSecurity就會使用該PasswordEncoder來進行密碼校驗。

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

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

2.4.3 登錄接口

我們需要自定義登陸接口,然后讓SpringSecurity對這個接口放行,讓用戶訪問這個接口的時候不用登錄也能訪問。 
? 在接口中我們通過AuthenticationManager的authenticate方法來進行用戶認證,所以需要在SecurityConfig中配置把AuthenticationManager注入容器。 
? 認證成功的話要生成一個jwt,放入響應(yīng)中返回。并且為了讓用戶下回請求時能通過jwt識別出具體的是哪個用戶,我們需要把用戶信息存入redis有效時間為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)認證
UsernamePasswordAuthenticationFilter.class);
    }
}

登錄接口代碼如下:

 
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    AuthenticationManager authenticationManager;
   @PostMapping("/user/login")
    public Result login(@RequestBody TbUser tbUser){
        //通過AuthenticationManager的authenticate方法來進行用戶認證
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(tbUser.getUsername(),tbUser.getPassword());
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        if(authenticate==null){
            return Result.error("401","登錄校驗失敗");
        }
        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認證過濾器

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

JwtAuthenticationTokenFilter自定義認證過濾器代碼如下:
@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()中所以后面的過濾器依舊認證失敗符合要求
        if(!StringUtils.hasText(token)){
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            return;
        }
        Long userId;
        try {
            //通過jwt工具類解析token獲得userId,如果token過期或非法就會拋異常
            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訪問其它方法就可以正常的進行訪問了,由此可見我們的代碼測試驗證成功。

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

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

相關(guān)文章

  • 基于Java實現(xiàn)考試管理系統(tǒng)

    基于Java實現(xiàn)考試管理系統(tǒng)

    這篇文章主要介紹了基于Java實現(xiàn)的考試管理系統(tǒng),項目運用到的技術(shù)有Springboot、Maven、Jpa、Vue等等,感興趣的小伙伴可以跟隨小編一起學習一下
    2021-12-12
  • springboot通過SchedulingConfigurer實現(xiàn)多定時任務(wù)注冊及動態(tài)修改執(zhí)行周期(示例詳解)

    springboot通過SchedulingConfigurer實現(xiàn)多定時任務(wù)注冊及動態(tài)修改執(zhí)行周期(示例詳解)

    這篇文章主要介紹了springboot通過SchedulingConfigurer實現(xiàn)多定時任務(wù)注冊及動態(tài)修改執(zhí)行周期,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • java Quartz定時器任務(wù)與Spring task定時的幾種實現(xiàn)方法

    java Quartz定時器任務(wù)與Spring task定時的幾種實現(xiàn)方法

    本篇文章主要介紹了java Quartz定時器任務(wù)與Spring task定時的幾種實現(xiàn)方法的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • 簡單易用的Spring?Boot郵件發(fā)送demo

    簡單易用的Spring?Boot郵件發(fā)送demo

    本文將介紹如何使用Spring?Boot發(fā)送郵件,我們將演示如何配置SMTP郵件服務(wù)器,創(chuàng)建一個郵件模板,以及如何使用JavaMailSender發(fā)送郵件,我們還將介紹如何測試我們的郵件發(fā)送代碼
    2023-12-12
  • JavaTCP上傳圖片代碼實例

    JavaTCP上傳圖片代碼實例

    今天小編就為大家分享一篇關(guān)于JavaTCP上傳圖片代碼實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • 如何解決Maven無法拉取SNAPSHOT依賴問題

    如何解決Maven無法拉取SNAPSHOT依賴問題

    在使用Maven管理項目時,可能會遇到無法拉取SNAPSHOT版本依賴的問題,這通常是因為Maven默認不支持直接拉取SNAPSHOT版本,遇到這樣的問題,可以通過在項目的pom.xml文件中添加<repositories>標簽,并配置啟用SNAPSHOT的倉庫地址來解決
    2024-10-10
  • IDEA使用maven創(chuàng)建hibernate項目的實現(xiàn)步驟(圖文)

    IDEA使用maven創(chuàng)建hibernate項目的實現(xiàn)步驟(圖文)

    本文主要介紹了IDEA使用maven創(chuàng)建hibernate項目的實現(xiàn)步驟,包括創(chuàng)建Maven項目,配置Hibernate,以及創(chuàng)建實體類映射到數(shù)據(jù)庫等步驟,具有一定的參考價值,感興趣的可以了解一下
    2023-08-08
  • Spring Boot項目如何同時支持HTTP和HTTPS協(xié)議的實現(xiàn)

    Spring Boot項目如何同時支持HTTP和HTTPS協(xié)議的實現(xiàn)

    這篇文章主要介紹了Spring Boot項目如何同時支持HTTP和HTTPS協(xié)議的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-10-10
  • java使用正則表達為數(shù)字添加千位符的簡單方法

    java使用正則表達為數(shù)字添加千位符的簡單方法

    這篇文章主要介紹了java使用正則表達為數(shù)字添加千位符的簡單方法,需要的朋友可以參考下
    2014-04-04
  • Java實例化類詳解

    Java實例化類詳解

    學習JAVA這門面向?qū)ο蟮恼Z言,實質(zhì)就是不斷地創(chuàng)建類,并把類實例化為對象并調(diào)用方法。對于初學JAVA的人總搞清楚對象是如何實例化的,假如類之間存在繼承關(guān)系,那就更糊涂了。下面我們通過兩個例題來說明對象的實例化過程。
    2016-03-03

最新評論