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

这篇具有很好参考价值的文章主要介绍了SpringSecurity实现前后端分离登录授权详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在介绍完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进入源码打断点如下图:

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

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

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

 关键代码分析:

private boolean hasAnyAuthorityName(String prefix, String... roles) {
        Set<String> roleSet = this.getAuthoritySet(); //从SecurityContextHolder.getContext()中获取用户的权限集合
        String[] var4 = roles; //这是我们测试方法中hasAuthority中的权限信息test1
        int 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 {
    @Override
    public 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 {
    @Override
    public 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 {
  
    @Autowired
    private AccessDenieHandleImpl accessDenieHandle;

    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;

http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) //配置认证失败处理器
                    .accessDeniedHandler(accessDenieHandle); //配置授权失败处理器
      http.cors() //允许跨域

    }

}

1.5 代码更改

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

   

MyUser类代码如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyUser implements UserDetails, Serializable {
    private TbUser tbuser;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> collect = tbuser.getPermissions().stream().map(SimpleGrantedAuthority::new).distinct().collect(Collectors.toList());
        return collect;
    }

    @Override
    public String getPassword() {
        return sysUser.getPassword();
    }

    @Override
    public String getUsername() {
        return sysUser.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
 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非法");
        }
        //根据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 {
    @Autowired
    TbUserMapper tbUserMapper;

    @Override
    public 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验证认证授权就此结束。

SpringSecurity实现前后端分离登录授权详解文章来源地址https://www.toymoban.com/news/detail-482547.html

到了这里,关于SpringSecurity实现前后端分离登录授权详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端uniapp+后端springboot 详细教程《实现微信小程序授权登录》(附完整前后端项目demo)

    微信小程序官方登录流程图: 参考微信小程序登录官网文档 1、前端技术栈 1.1、uniapp 使用uniapp构建一套代码多端使用的前端框架项目 1.2、前端封装工具 dateUtil.js: 功能: 1. 时间日期格式化 2. 传入日期是否和当前日期的比较 完整代码: requestUtil.js: 功能: 1. 定义公共的

    2024年02月14日
    浏览(59)
  • SpringSecurity6+JWT实现前后端分离

    本文使用mybatis-plus作为ORM框架,然后使用springsecurity6作为安全框架,采用JWT作为权限确认的方式。 其实我现在就刚入门这个springsecurity6而已,有许多都没有摸清它的使用,写下这篇文章只是想记录一下,下次想用的时候来看看。 上面说了使用JWT。那就得有一个能生成JWT和检

    2024年02月22日
    浏览(40)
  • SSM+Shiro安全框架整合(完成安全认证--登录+权限授权)+ssm整合shiro前后端分离

    目录 1.搭建SSM框架  1.1.引入相关的依赖 1.2. spring配置文件 1.3. web.xml配置文件 1.4.配置Tomcat并启动 2.ssm整合shiro---认证功能  (1).引入依赖 (2).修改spring配置文件 (3).修改web.xml文件 (4).新建login.jsp(登录页面) (5).新建success.jsp(登录成功后跳转到此) (6).创建User实体类 (7).创建LoginVo

    2024年02月15日
    浏览(46)
  • 若依前后端分离项目集成CAS 5.3实现单点登录

    一.获取CAS5.3项目资源 GitHub - apereo/cas-overlay-template at 5.3 cas5.3.x还是基于jdk8运行,下一个版本6.0.x就基于jdk9了,随着cas版本升级要求jdk版本也越来越高,官网上和github上都有每个版本基本运行条件的说明,根据实际情况选择版本。 二.tomcat部署 利用maven-package打war包 2.将war包放

    2024年02月02日
    浏览(92)
  • uniapp 小程序实现微信授权登录(前端和后端)

    1.主要流程:先通过 uni.getUserProfile授权获取用户名称和头像等信息 在调用 uni.login 获取微信登录需要的临时code 2. 前端代码: 1.主要流程:在前端调用接口成功的将临时code 传递给后端时 ,后端通过调用微信的第三方接口拿到 openid, session_key 这两个参数,查询数据库是否有

    2024年02月16日
    浏览(44)
  • Springboot +spring security,实现前后端分离,使用JSON数据格式登录(将表单提交方式改成json格式登录)

    在前面的文章中,我们使用表单方式完成登录提交,但是目前基本都是前后端分离项目,很少使用表单提交的方式,基本都是json方式,使用ajax提交,那么我们怎么将表单提交方式改成json格式登录呢? 通过前面源码部分学习中,已经知道在HttpSecurity配置中,每新增一种配置,

    2024年02月06日
    浏览(48)
  • 前后端分离------后端创建笔记(02)

     本文章转载于【SpringBoot+Vue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论,如有侵权请联系 源码:https://gitee.com/green_vegetables/x-admin-project.git 素材:https://pan.baidu.com/s/1ZZ8c-kRPUxY6FWzsoOOjtA 提取码:up4c 项目概述笔记:https://blog

    2024年02月12日
    浏览(37)
  • SpringSecurity认证和授权流程详解

    Spring Security是一个Java框架,用于保护应用程序的安全性。它提供了一套全面的安全解决方案,包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念,可以轻松地集成到任何基于Spring的应用程序中。它支持多种身份验证选项和授权策略,开发人员可以根据需

    2024年04月08日
    浏览(50)
  • SpringBoot3.0 + SpringSecurity6.0 +OAuth2 使用Github作为授权登录

    1.1 OAuth是什么 开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。 OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供

    2024年02月11日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包