亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

vue3整合SpringSecurity加JWT實(shí)現(xiàn)權(quán)限校驗(yàn)

 更新時(shí)間:2025年04月13日 11:25:58   作者:張喬24  
本文主要介紹了vue3整合SpringSecurity加JWT實(shí)現(xiàn)權(quán)限校驗(yàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

我本來是想著登錄認(rèn)證和權(quán)限校驗(yàn)放在一篇文章里的,但是上次寫登錄認(rèn)證就寫了非常多了,實(shí)在是有些寫不動(dòng)了,所以才分為了兩篇文章。

本文適合有一定基礎(chǔ)的人來看,如果你對springsecurity安全框架還不是很了解,建議你先去看一下我之前寫過的spring security框架的快速入門:

springboot3整合SpringSecurity實(shí)現(xiàn)登錄校驗(yàn)與權(quán)限認(rèn)證(萬字超詳細(xì)講解)

技術(shù)棧版本:vue3.3.11、springboot3.1.5、spring security6.x

之前的登錄認(rèn)證文章:
前后端分離,使用vue3整合SpringSecurity加JWT實(shí)現(xiàn)登錄認(rèn)證

在上次的文章中,只寫到登錄成功和退出之后就不寫了,這次會(huì)加上權(quán)限校驗(yàn)。

首先,在原來數(shù)據(jù)庫的基礎(chǔ)上再新建:角色表、權(quán)限表、用戶角色表、角色權(quán)限表四張表:

2、角色表

CREATE TABLE roles (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL UNIQUE,
  description VARCHAR(255),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

3、權(quán)限表

CREATE TABLE permissions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL UNIQUE,
  description VARCHAR(255),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

4、用戶角色表

CREATE TABLE user_roles (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  role_id INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREIGN KEY (role_id) REFERENCES roles(id)
);

5、角色權(quán)限表

CREATE TABLE role_permissions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  role_id INT NOT NULL,
  permission_id INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (role_id) REFERENCES roles(id),
  FOREIGN KEY (permission_id) REFERENCES permissions(id)
);

現(xiàn)在,我們的數(shù)據(jù)庫中共有5張表,分別創(chuàng)建相應(yīng)的server、mapper和controller層。

接下來,再原來的登錄認(rèn)證的代碼的基礎(chǔ)上就可以來實(shí)現(xiàn)我們的權(quán)限校驗(yàn)了;

權(quán)限校驗(yàn)這方面主要體現(xiàn)在后端代碼上,所以前端我只是進(jìn)行一些簡單的演示即可;

1、在我們的MyTUserDetail類中定義角色和權(quán)限的屬性集合,并添加到UserDetails類的getAuthorities方法中(角色和權(quán)限我都使用Set定義,這樣能夠去重)

代碼如下:

@Data
public class MyUserDetail implements Serializable, UserDetails {

    private static final long serialVersionUID = 1L;

    private Users Users;

//    角色
    private Set<String> roles;

//    權(quán)限
    private  Set<String> permissions;

    @JsonIgnore  //json忽略
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        List<GrantedAuthority> list =  new ArrayList<>();

//        如果角色不用空,則將角色添加到list中
        if (!ObjectUtils.isEmpty(roles)){
            roles.forEach(role->list.add(new SimpleGrantedAuthority("ZML_"+role)));
        }

//                如果權(quán)限不用空,則將權(quán)限添加到list中
        if (!ObjectUtils.isEmpty(permissions)){
            permissions.forEach(permission->list.add(new SimpleGrantedAuthority(permission)));
        }
        return list;
        
    }

    @JsonIgnore
    @Override
    public String getPassword() {
        return this.getUsers().getPassword();
    }
    @JsonIgnore
    @Override
    public String getUsername() {
        return this.getUsers().getUsername();
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return this.getUsers().getStatus()==0;
    }
    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return this.getUsers().getStatus()==0;
    }
    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return this.getUsers().getStatus()==0;
    }
    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return this.getUsers().getStatus()==0;
    }
}

Authentication 討論了所有 Authentication 實(shí)現(xiàn)如何存儲(chǔ) GrantedAuthority 對象的列表。這些對象代表已經(jīng)授予委托人(principal)的權(quán)限。GrantedAuthority 對象由 AuthenticationManager 插入到 Authentication 對象中,隨后由 AccessDecisionManager 實(shí)例在做出授權(quán)決定時(shí)讀取。

GrantedAuthority 接口只有一個(gè)方法。

String getAuthority();

這個(gè)方法被 AuthorizationManager 實(shí)例用來獲取 GrantedAuthority 的一個(gè)精確的 String 表示。通過返回一個(gè) String 表示,一個(gè) GrantedAuthority 可以被大多數(shù) AuthorizationManager 實(shí)現(xiàn)輕松 "讀取"。如果 GrantedAuthority 不能被精確地表示為一個(gè) String,那么該 GrantedAuthority 被認(rèn)為是 "復(fù)雜的",getAuthority() 必須返回 null。

一個(gè)復(fù)雜的 GrantedAuthority 的例子是一個(gè)實(shí)現(xiàn),它存儲(chǔ)了一個(gè)適用于不同客戶賬號(hào)的操作和權(quán)限閾值的列表。將這種復(fù)雜的 GrantedAuthority 表示為一個(gè) String 將是相當(dāng)困難的。因此,getAuthority() 方法應(yīng)該返回 null。這向任何 AuthorizationManager 表明,它需要支持特定的 GrantedAuthority 實(shí)現(xiàn)來理解其內(nèi)容。

Spring Security 包括一個(gè)具體的 GrantedAuthority 實(shí)現(xiàn)。SimpleGrantedAuthority。這個(gè)實(shí)現(xiàn)允許任何用戶指定的字符串被轉(zhuǎn)換為 GrantedAuthority。安全架構(gòu)中包含的所有 AuthenticationProvider 實(shí)例都使用 SimpleGrantedAuthority 來填充 Authentication 對象。

默認(rèn)情況下,基于角色的授權(quán)規(guī)則包括 ROLE_ 作為前綴。這意味著,如果有一個(gè)授權(quán)規(guī)則要求 security context 的角色是 "USER",Spring Security 將默認(rèn)尋找返回 "ROLE_USER" 的 GrantedAuthority#getAuthority。

你可以用 GrantedAuthorityDefaults 來定制這個(gè)。GrantedAuthorityDefaults 的存在是為了允許自定義基于角色的授權(quán)規(guī)則所使用的前綴。

你可以通過暴露一個(gè) GrantedAuthorityDefaults Bean 來配置授權(quán)規(guī)則以使用不同的前綴,像這樣:

@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
	return new GrantedAuthorityDefaults("ZML_");
}

我們需要特別注意的一點(diǎn)是,在spring security中。我們的角色和權(quán)限是存儲(chǔ)在一起的,沒有分開存儲(chǔ) 如:

 參考來源:授權(quán)架構(gòu) :: Spring Security Reference

2、在MyUserDetailServerImpl類的loadUserByUsername方法中查出登錄用戶的權(quán)限集合:

代碼如下:

@Service
@Slf4j
public class MyUserDetailServerImpl implements MyUserDetailServer {
    @Autowired
    UsersMapper userService;

    /**
     * 返回一個(gè)賬號(hào)所擁有的權(quán)限碼集合
     */

//    角色權(quán)限表
    @Autowired
    IRolePermissionsService rolePermissionsService;
    //    用戶角色表
    @Autowired
    IUserRolesService userRolesService;
    //權(quán)限表
    @Autowired
    IPermissionsService permissionsService;

    //    角色表
    @Autowired
    IRolesService rolesService;



    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Users users = userService.selectOne(new LambdaQueryWrapper<Users>().
                eq(username != null, Users::getUsername, username));
        if (users == null) {
            throw new UsernameNotFoundException("用戶名不存在");
        }
log.info("UserDetailServer中的user:=========>"+users);
        MyUserDetail myTUserDetail=new MyUserDetail();
myTUserDetail.setUsers(users);


        // 查詢用戶權(quán)限


//        根據(jù)用戶id從用戶角色表中獲取角色id
        List<UserRoles> roleIds = userRolesService.list(new LambdaQueryWrapper<UserRoles>()
                .eq(UserRoles::getUserId,users.getId()));
        List<Integer> rolesList = roleIds.stream().map(UserRoles::getRoleId).toList();

        if (!(roleIds.size() >0)){
//            用戶沒有分配角色
            return myTUserDetail;
        }
        
        Set<String> listPermission = new HashSet<>();

        rolesList.forEach(roleId ->{
            // 根據(jù)角色id從角色權(quán)限表中獲取權(quán)限id
            List<RolePermissions> rolePermissions = rolePermissionsService.list(new LambdaQueryWrapper<RolePermissions>().
                    eq(RolePermissions::getRoleId, roleId));
            // 根據(jù)權(quán)限id從權(quán)限表中獲取權(quán)限名稱
            rolePermissions.forEach(permissionsId->{
                Permissions permissions = permissionsService.getById(permissionsId.getPermissionId());
                listPermission.add(permissions.getName());
            });
        });

myTUserDetail.setPermissions( listPermission);

        // 查詢角色角色

        Set<String> listRole = new HashSet<>();

     
        
        roleIds.forEach(roleId ->{
            Roles byId = rolesService.getById(roleId.getRoleId());
            listRole.add(byId.getName());
        });
        myTUserDetail.setRoles(listRole);
log.info("UserDetailServer中的查完權(quán)限的myTUserDetail:=========>"+myTUserDetail);
        return myTUserDetail;
    }

}

我所實(shí)現(xiàn)的是標(biāo)準(zhǔn)的RBAC(基于用戶、角色、權(quán)限的訪問控制模型)。所以,在得到用戶id的情況下、先根據(jù)用戶角色表查出角色id(如果角色id的集合為空,說明用戶沒有分配任何角色,直接返回用戶信息)、在根據(jù)角色權(quán)限表查詢權(quán)限id,在根據(jù)權(quán)限表查出具體權(quán)限名稱。

上面使用了Mybatis-plus的條件構(gòu)造器和stream流的形式進(jìn)行查詢。

3、在JwtAuthenticationTokenFilter攔截器中,在查詢到用戶信息時(shí),將用戶的標(biāo)識(shí)和用戶擁有的權(quán)限一起放到SecurityContextHolder中,這樣后面的過濾器在獲取到用戶信息的同時(shí)也能獲取到用戶所擁有的權(quán)限;

代碼如下:

@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //獲取請求頭中的token
        String token = request.getHeader("token");
        System.out.println("前端的token信息=======>"+token);
        //如果token為空直接放行,由于用戶信息沒有存放在SecurityContextHolder.getContext()中所以后面的過濾器依舊認(rèn)證失敗符合要求
        if(!StringUtils.hasText(token)){
            filterChain.doFilter(request,response);
            return;
        }

//        解析Jwt中的用戶id
        Integer userId = jwtUtil.getUsernameFromToken(token);
        //從redis中獲取用戶信息
        String redisUser = redisTemplate.opsForValue().get(String.valueOf(userId));
        if(!StringUtils.hasText(redisUser)){
            filterChain.doFilter(request,response);
            return;
        }

        MyUserDetail myTUserDetail= JSON.parseObject(redisUser, MyUserDetail.class);
log.info("Jwt過濾器中MyUserDetail的值============>"+myTUserDetail.toString());

        //將用戶信息存放在SecurityContextHolder.getContext(),后面的過濾器就可以獲得用戶信息了。這表明當(dāng)前這個(gè)用戶是登錄過的,后續(xù)的攔截器就不用再攔截了
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(myTUserDetail,null,myTUserDetail.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        filterChain.doFilter(request,response);
    }
}

在這里解釋一下UsernamePasswordAuthenticationToken類:

UsernamePasswordAuthenticationToken是Spring Security中用于表示基于用戶名和密碼的身份驗(yàn)證令牌的類。它主要有以下兩個(gè)構(gòu)造方法:

  • UsernamePasswordAuthenticationToken(Object principal, Object credentials)

    • principal參數(shù)表示認(rèn)證主體,通常是用戶名或用戶對象。在身份驗(yàn)證過程中,這通常是用來標(biāo)識(shí)用戶的信息,可以是用戶名、郵箱等。
    • credentials參數(shù)表示憑據(jù),通常是用戶的密碼或其他憑證信息。在身份驗(yàn)證過程中,這用于驗(yàn)證用戶的身份。
  • UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities)

    • 除了上述兩個(gè)參數(shù)外,這個(gè)構(gòu)造方法還接受一個(gè)授權(quán)權(quán)限集合(authorities參數(shù))。這個(gè)集合表示用戶所擁有的權(quán)限,通常是一個(gè)包含用戶權(quán)限信息的集合。
    • GrantedAuthority接口代表了用戶的權(quán)限信息,可以通過該接口的實(shí)現(xiàn)類來表示用戶具體的權(quán)限。

這兩個(gè)構(gòu)造方法的作用是創(chuàng)建一個(gè)包含用戶身份信息、憑據(jù)信息和權(quán)限信息的身份驗(yàn)證令牌,以便在Spring Security中進(jìn)行身份驗(yàn)證和授權(quán)操作。通過這些構(gòu)造方法,可以將用戶的相關(guān)信息封裝成一個(gè)完整的身份驗(yàn)證對象,方便在安全框架中進(jìn)行處理和驗(yàn)證。

總之,UsernamePasswordAuthenticationToken是在Spring Security中用于表示用戶名密碼身份驗(yàn)證信息的重要類,通過不同的構(gòu)造方法可以滿足不同場景下的需求

所以我們通過myTUserDetail.getAuthorities()方法完全可以將用戶擁有的權(quán)限方法Security容器中,并供后續(xù)的攔截器獲取用戶信息和權(quán)限;

4、運(yùn)行測試:
接下來我編寫一個(gè)基于方法的權(quán)限校驗(yàn),看我們編寫的代碼是否生效;

(基于方法的權(quán)限認(rèn)證要在SecurityConfig類上加上@EnableMethodSecurity注解,表示開啟了方法權(quán)限的使用;)

新建一個(gè)TestController,并在這個(gè)類中定義一個(gè)方法,用來測試:

@RestController
@RequestMapping("/test")
public class TestController {
    @PreAuthorize("hasAnyAuthority('所有權(quán)限')")
 @GetMapping("/hello")
    public Result hello(){
        System.out.println("test接口中的hello方法調(diào)用========================");
        return Result.successData("hello");
    }

}

在前端的Layout.vue頁面中新增一個(gè)按鈕,并綁定指定的方法用來測試;

代碼如圖:

const testHello = async() => {
  let data:any= await api.get("/test/hello")
if(data.code===200){
  ElMessage('有權(quán)限')
}
else{
  ElMessage.error('沒有權(quán)限')
}
}

現(xiàn)在,我們來測試看看這個(gè)方法能不能被調(diào)用到:

可以看到這個(gè)方法被正確的訪問到了,這是必須的因?yàn)檫@個(gè)”張喬“用戶有這個(gè)權(quán)限,那么我們改一下所需的權(quán)限看還能不能訪問到;

點(diǎn)擊前端按鈕:

可以看到確實(shí)不能訪問到了,這說明我們的代碼是正確的;

我們權(quán)限校驗(yàn)的邏輯是:直接在登錄時(shí)查詢用戶的權(quán)限,并放在我們自定義的實(shí)現(xiàn)了UserDetail的接口類中(MyUserDetail),用來表示登錄用戶的全部信息;

至此:我們前后端分離,使用vue3整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和權(quán)限校驗(yàn)就已經(jīng)全部的講解完畢了,我還是會(huì)將前后端的源碼放在碼云上,有需要的童靴可以自行的下載:

碼云地址:
Vue-Security: 前后端分離的Security

到此這篇關(guān)于vue3整合SpringSecurity加JWT實(shí)現(xiàn)權(quán)限校驗(yàn)的文章就介紹到這了,更多相關(guān)vue3 SpringSecurity 權(quán)限校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論