SpringSecurity实现前后端分离登录授权详解

news/2024/10/17 15:26:56/

在介绍完SpringSecurity实现前后端分离认证之后,然后就是SpringSecurity授权,在阅读本文章之前可以先了解一下作者的上一篇文章SpringSecurity认证SpringSecurity实现前后端分离登录token认证详解_山河亦问安的博客-CSDN博客。

目录

1. 授权

1.1 权限系统的作用

1.2 授权基本流程

1.3 授权实现

1.3.1 限制访问资源所需权限

1.3.2 权限校验方法

1.3.3 自定义权限校验方法

1.4 自定义失败方案

1.5 代码更改


1. 授权

1.1 权限系统的作用

例如一个学校图书馆的管理系统,如果是普通学生登录就能看到借书还书相关的功能,不可能让他看到并且去使用添加书籍信息,删除书籍信息等功能。但是如果是一个图书馆管理员的账号登录了,应该就能看到并使用添加书籍信息,删除书籍信息等功能。
总结起来就是不同的用户可以使用不同的功能。这就是权限系统要去实现的效果。
我们不能只依赖前端去判断用户的权限来选择显示哪些菜单哪些按钮。因为如果只是这样,如果有人知道了对应功能的接口地址就可)不通过前端,直接去发送请求来实现相关功能操作。
所以我们还需要在后台进行用户权限的判断,判断当前用户是否有相应的权限,必须基于所需权限才能进行相应的操作。


1.2 授权基本流程

SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。所以我们在项目中只需要把当前登录用户的权限信息也存入Authentication。然后设置我们的资源所需要的权限即可。
 

1.3 授权实现

1.3.1 限制访问资源所需权限

SpringSecurity为我们提供了基于注解的权限控制方案,这也是我们项目中主要采用的方式。我们可以使用注解去指定访问对应的资源所需的权限。 ​ 但是要使用它我们需要先开启相关配置。我们需要在springSecurity配置类上加下面这行代码:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}

@EnableGlobalMethodSecurity 注解参数说明:

  • prePostEnabled = true 会解锁 @PreAuthorize 和 @PostAuthorize 两个注解。顾名思义,@PreAuthorize 注解会在方法执行前进行验证,而 @PostAuthorize 注解会在方法执行后进行验证。
  • securedEnabled = true 会解锁 @Secured 注解。@Secured 是专门用于判断是否具有角色的。能写在方法或类上。参数要以 ROLE_开头。

1.3.2 权限校验方法

hasAuthority(String)

判断用户是否具有特定的权限

    @PostMapping("/test1")@PreAuthorize("hasAuthority('test1')")public String test1(){return "test";}

以上面代码为例,按Ctrl+Alt,然后点击上面的hasAuthority进入源码打断点如下图:

 利用Apipost发送请求得到的结果如下图:

 关键代码分析:

private boolean hasAnyAuthorityName(String prefix, String... roles) {Set<String> roleSet = this.getAuthoritySet(); //从SecurityContextHolder.getContext()中获取用户的权限集合String[] var4 = roles; //这是我们测试方法中hasAuthority中的权限信息test1int var5 = roles.length;for(int var6 = 0; var6 < var5; ++var6) {String role = var4[var6];String defaultedRole = getRoleWithDefaultPrefix(prefix, role); //遍历var4数组,其中的每个权限信息比如test1加上前缀,比如pre+test1,这里pre为null,如果roleset中包含这个权限信息就会返回true,该方法就会正常进行(roleSet.contains(defaultedRole)) {return true;}}return false;}

hasAnyAuthority(String …)

hasAnyAuthority方法可以传入多个权限,只有用户有其中任意一个权限都可以访问对应资源。

    @PostMapping("/test")@PreAuthorize("hasAuthority('test1','test')")public String tes1t(){return "test";}

 hasRole(String)

hasRole要求有对应的角色才可以访问,但是它内部会把我们传入的参数拼接上 ROLE_ 后再去比较。所以这种情况下要用用户对应的权限也要有 ROLE_ 这个前缀才可以。

   @PreAuthorize("hasRole('test')")public String hello(){return "hello";}

hasAnyAuthority(String …)

hasAnyRole 有任意的角色就可以访问。它内部也会把我们传入的参数拼接上 ROLE_ 后再去比较。所以这种情况下要用用户对应的权限也要有 ROLE_ 这个前缀才可以。

1.3.3 自定义权限校验方法

​ 我们也可以定义自己的权限校验方法,在@PreAuthorize注解中使用我们的方法。代码如下:

@Component("ex")
public class SGExpressionRoot {public boolean hasAuthority(String authority){Authentication authentication = SecurityContextHolder.getContext().getAuthentication();MyUser myUser = (MyUser) authentication.getPrincipal();List<String> permissions = myUser.getTbUser().getPermissions();return permissions.contains(authority);}
}

 在SPEL表达式中使用 @ex相当于获取容器中bean对象。然后再调用这个对象的hasAuthority方法,代码如下:

    @PostMapping("/test")@PreAuthorize("@ex.hasAuthority('test')")public TbUser test(){TbUser tbUser = tbUserMapper.selectById(1);return tbUser;}


1.4 自定义失败方案

我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json,这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurity的异常处理机制。 在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。 ​

如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。  代码如下:

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {Result result = Result.error("401", "用户认证失败,请重新登录");response.setStatus(200);response.setContentType("application/json");response.setCharacterEncoding("utf-8");response.getWriter().print(result.toString());}
}

如果是授权过程中出现的异常会被封装成AccessDeniedException,然后调用AccessDeniedHandler对象的方法去进行异常处理。 代码如下:

@Component
public class AccessDenieHandleImpl implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {Result result = Result.error("403", "用户权限不足");response.setStatus(200);response.setContentType("application/json");response.setCharacterEncoding("utf-8");response.getWriter().print(result.toString());}
}


​ 所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置给SpringSecurity即可。配置代码如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {@Autowiredprivate AccessDenieHandleImpl accessDenieHandle;@Autowiredprivate AuthenticationEntryPointImpl authenticationEntryPoint;http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) //配置认证失败处理器.accessDeniedHandler(accessDenieHandle); //配置授权失败处理器http.cors() //允许跨域}}

1.5 代码更改

在认证的基础上添加授权,为了方便这里的权限信息设计写死,在真正的项目中每个用户的权限应该从数据库中进行查询。代码设计如下:

 JwtAuthenticationTokenFilter类代码如下:
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@AutowiredRedisTemplate redisTemplate;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {//获取请求头中的tokenString 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非法");}//根据userId从redis中获取用户信息,如果没有该用户就代表该用户没有登录过TbUser myUser = (TbUser) redisTemplate.opsForValue().get(String.valueOf(userId));if(Objects.isNull(myUser)){throw new RuntimeException("用户未登录");}MyUser myUser1=new MyUser(myUser);//将用户信息存放在SecurityContextHolder.getContext(),后面的过滤器就可以获得用户信息了。UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(myUser1,null,myUser1.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);filterChain.doFilter(httpServletRequest,httpServletResponse);}
}
UserDetailServiceImpl类
@Service
public class UserDetailServiceImpl implements UserDetailsService {@AutowiredTbUserMapper tbUserMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {QueryWrapper<TbUser> queryWrapper=new QueryWrapper<>();queryWrapper.eq("username",username);TbUser tbUser = tbUserMapper.selectOne(queryWrapper);List<String> list = Arrays.asList("test");tbUser.setPermissions(list);if(tbUser==null){throw new RuntimeException("用户名或者密码错误");}return new MyUser(tbUser);}
}

至此SpringSecurity实现前后端分离token验证认证授权就此结束。


http://www.ppmy.cn/news/386871.html

相关文章

论文解读 | 基于蒙特卡罗树搜索的触觉目标识别主动末端执行器姿态选择

原创 | 文 BFT机器人 本论文提出了一种新颖的方法&#xff0c;使用仅触觉来进行主动物体识别。该算法通过蒙特卡罗树搜索来选择最佳的手腕姿态序列进行物体识别。 具体来说&#xff0c;该算法将问题建模为马尔可夫决策过程&#xff08;MDP&#xff09;&#xff0c;并通过观察和…

vr消防隐患排查软件应用到加油站的好处

传统的隐患辨识排查安全培训方式主要以理论培训为主&#xff0c;现有的实操培训力量较弱且加油站涉及危险环境现有的实操培训难以满足实际需求&#xff0c;如何高效进行加油站安全隐患辨识与排查? 加油站火灾VR安全隐患排查系统是一种基于VR虚拟现实技制作术的智能化安全检查工…

pandas---缺失值的处理

1. 处理缺失值 判断数据中是否包含NaN&#xff1a; pd.isnull(df)&#xff1b;pd.notnull(df) 存在缺失值nan: 删除存在缺失值的:dropna(axisrows) 不会修改原数据&#xff0c;需要接受返回值&#xff1b; 替换缺失值:fillna(value, inplaceTrue) value:替换成的值&#…

manjaro安装nvidia显卡驱动

现在manjaro提供video-hyprid-intel-nvidia-prime的闭源显卡驱动&#xff0c;首先在在硬件设定中卸掉video-linux开源驱动&#xff0c;安装prime驱动 安装相关驱动&#xff1a; sudo pacman -S nvidia opencl-nvidia lib32-nvidia-utils lib32-opencl-nvidia mesa lib32-mesa…

ubuntu如何重新安装NVIDIA显卡驱动

打开命令行 将原驱动卸载 sudo apt-get remove nvidia* # 卸载 关闭显示管理程序light-dm sudo service lightdm stop # 关闭lightdm服务 安装显卡驱动 sudo ./NVIDIA-Linux-x86_64-440.36.run # 安装驱动&#xff0c;执行界面默认选项回车 唤醒显示管理display manager sudo s…

ubuntu nvidia显卡驱动failed解决方法

1、问题&#xff1a; ubuntu 系统安装nvidia driver后&#xff0c;查看显卡信息显示failed。 报错信息&#xff1a; #查看显卡信息 useranonymous_address:~$ nvidia-smi NVIDIA-SMI has failed because it couldnt communicate with the NVIDIA driver. Make sure that the…

Ubuntu16.04 LTS安装NVIDIA显卡驱动

前言 1.一直在Linux下做机器学习相关的工作&#xff0c;避免不了要使用到cuda库进行加速&#xff0c;NVIDIA对Linux的支持并不友好&#xff0c;在安装和配置的过程中踩了不少坑&#xff0c;所以有必要做个记录和总结&#xff0c;免得下次自己又踩到同样的坑。 2.我的安装的是D…

安装nvidia显卡驱动,Debian11

两个步骤&#xff1a; 1&#xff1a;关闭禁止nouveau驱动 2&#xff1a; 安装nvidia驱动&#xff08;要先下载好NVIDIA驱动&#xff0c;下载地址&#xff09; 步骤1&#xff1a;网上资料 &#xff08;1&#xff09;&#xff1a;在/etc/modprobe.d/blacklist.conf 里面添加内容…