返回顶部
首页 > 资讯 > 后端开发 > JAVA >SpringSecurity实现前后端分离登录token认证详解
  • 531
分享到

SpringSecurity实现前后端分离登录token认证详解

springjavaspringboot 2023-08-31 13:08:43 531人浏览 安东尼
摘要

目录 1. SpringSecurity概述 1.1 权限框架 1.1.1 Apache Shiro 1.1.2 SpringSecurity 1.1.3 权限框架的选择 1.2 授权和认证 1.3 SpringSecurity的功能

目录

1. SpringSecurity概述

1.1 权限框架

1.1.1 Apache Shiro

1.1.2 SpringSecurity

1.1.3 权限框架的选择

1.2 授权和认证

1.3 SpringSecurity的功能

2.SpringSecurity 实战

2.1 引入SpringSecurity

2.2 认证

2.2.1 登录校验流程

 2.2.2 SpringSecurity完整流程

 2.2.3 认证流程详解

2.3 思路分析

2.4 代码实战

2.4.1  自定义UserDetailsService类

2.4.2 密码加密存储

2.4.3 登录接口

2.4.4 token认证过滤器

2.4.5 接口测试


1. springSecurity概述

1.1 权限框架

目前市面上比较流行的权限框架主要实shiro和Spring Security,这两个框架各自侧重点不同,各有各的优劣。

1.1.1 Apache Shiro

Apache Shiro(读作"sheeroh”,即日语"城")是一个开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。

特点:

Shiro的特点:
1. 易于理解的Java Security APl;
2. 简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory等);·
3. 对角色的简单的签权(访问控制),支持细粒度的签权;
4. 支持一级缓存,以提升应用程序的性能;
5. 内置的基于POJO企业会话管理,适用于WEB 以及非 Web的环境;
6.异构客户端会话访问;
7.非常简单的加密api;
8.不跟任何的框架或者容器捆绑,可以独立运行。

1.1.2 SpringSecurity

Spring Security是一个能够为基于Spring的企业应用系统提供描述性安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring ioc(依赖注入,也称控制反转)和aop(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Spring Security是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比shiro丰富。一般Web应用的需要进行认证和授权。而认证和授权也是SpringSecurity作为安全框架的核心功能。

SpringSecurity的特点:

Spring Boot集成非常简单。
2. 功能强大,高度可定制化。
3. 支持OAuth2.0。
4. 强大的加密ARI。
5. 防止跨站请求伪造攻击(CSRF)。
6. 提供spring cloud分布式组件。

1.1.3 权限框架的选择

Spring Security 是 Spring 家族中的一个安全管理框架,实际上,在 Spring Boot 出现之前,Spring Security 就已经发展了多年了,但是使用的并不多,安全管理这个领域,一直是 Shiro 的天下。
相对于 Shiro,在 SSM 中整合 Spring Security 都是比较麻烦的操作,所以,SpringSecurity 虽然功能比 Shiro 强大,但是使用反而没有 Shiro 多(Shiro 虽然功能没有Spring Security 多,但是对于大部分项目而言,Shiro 也够用了)。自从有了 Spring Boot 之后,Spring Boot 对于 Spring Security 提供了自动化配置方案,可以使用更少的配置来使用 Spring Security。
 

1.2 授权和认证

一般来说,Web应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是SpringSecurity重要核心功能。

1)用户认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。


(2)用户授权:是验证某个用户是否有权限执行某个操作。经过认证后判断当前用户是否有权限进行某个操作。

下面进行介绍一下RBAC:

RBAC (Role Based Access Control)基于角色的访问控制,通过抽象出“用户、角色、权限”"三个概念,实现用户分配角色,角色分配权限的权限管理方式,也是目前企业中权限管理主要实现方案。

案例如下图所示:

1.3 SpringSecurity的功能

Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。

如今的Spring Security已经成为Spring Framework下最成熟的安全系统,它为我们提供了强大而灵活的企业级安全服务,如:

    Ø        认证授权机制

    Ø         Web资源访问控制

    Ø        业务方法调用访问控制

    Ø        领域对象访问控制Access Control List(ACL)

    Ø        单点登录(Central Authentication Service)

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

2.SpringSecurity 实战

2.1 引入SpringSecurity

导入依赖

                    org.springframework.boot            spring-boot-starter-security        

引入依赖后我们在尝试去访问之前的接口就会自动跳转到一个SpringSecurity的默认登陆页面,默认用户名是user,密码会输出在控制台。必须登陆之后才能对接口进行访问。
 

2.2 认证

2.2.1 登录校验流程

登录校验流程如下图所示:

 2.2.2 SpringSecurity完整流程

SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。这里我们可以看看入门案例中的过滤器。

 

 UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请求。入门案例的认证工作主要有它负责。

ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException。

FilterSecurityInterceptor:负责权限校验的过滤器。


我们可以通过Debug查看当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序。如下图所示:

 2.2.3 认证流程详解

Authentication接口:它的实现类,表示当前访问系统的用户,封装了用户相关信息。

AuthenticationManager接口:定义了认证Authentication的方法。

UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。

UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。
 

2.3 思路分析

在前后端分离中我们一般采用token验证,所以在这里我们要自定义登录接口,通过自定义接口调用调用ProviderManager的方法进行认证 ,如果认证通过生成Jwt,然后将jwt返回给前端,同时将用户信息包括用户权限信息等存入到Redis数据库中,redis数据库作为缓存读取速度远远大于从数据库中进行读取用户信息。我们还要自定义UserDetailsService,在这个实现类中去查询数据库。除此之外我们还需要定义JWT认证过滤器,从前端请求头中获取token,解析token获取其中的userid,然后根据userid从redis中获取用户信息存入SecurityContextHolder。具体流程如下图:

2.4 代码实战

2.4.1  自定义UserDetailsService类

首先定义一个UserDetails类代码如下:

@Data@AllArgsConstructor@NoArgsConstructorpublic class MyUser implements UserDetails, Serializable {    private TbUser user; //用户信息实体类,里面存储着用户的账号,密码,权限信息    //返回权限信息    @Override    public Collection 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;    }}

自定义实现UserDetailsService接口,重写其中的方法。代码如下:

@Servicepublic class UserDetailServiceImpl implements UserDetailsService {    @Autowired    TbUserMapper tbUserMapper;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        //根据用户名从数据库中查询数据信息        QueryWrapper 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 密码加密存储

实际项目中我们不会把密码明文存储在数据库中。 
​1. 默认使用的PassWordEncoder要求数据库中的密码格式为:{id}password 。它会根据id去判断密码的加密方式。但是我们一般不会采用这种方式。所以就需要替换PasswordEncoder。​

2.我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder。

3.我们只需要使用把BCryptPasswordEncoder对象注入Spring容器中,SpringSecurity就会使用该PasswordEncoder来进行密码校验。

4.​我们可以定义一个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承WebSecurityConfigurerAdapter。

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

2.4.3 登录接口

我们需要自定义登陆接口,然后让SpringSecurity对这个接口放行,让用户访问这个接口的时候不用登录也能访问。 
​ 在接口中我们通过AuthenticationManager的authenticate方法来进行用户认证,所以需要在SecurityConfig中配置把AuthenticationManager注入容器。 
​ 认证成功的话要生成一个jwt,放入响应中返回。并且为了让用户下回请求时能通过jwt识别出具体的是哪个用户,我们需要把用户信息存入redis有效时间为30分钟,可以把用户id作为key。登录接口代码如下:

Configuration代码如下:

@Configurationpublic 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()//关闭csrf                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不通过session获取SecurityContext                .and()                .authorizeRequests()                .antMatchers("/user/login").anonymous() //对于登录接口允许匿名访问                .anyRequest().authenticated(); //除上面外的所有请求全部需要鉴权认证    }}

登录接口代码如下:

    @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();            //根据用户id生成token            String token = JwtUtil.generateToken(id);            //将token放在数据库中            redisTemplate.opsForValue().set(String.valueOf(id),myUser,30, TimeUnit.MINUTES);            return Result.success(token);        }    }

2.4.4 token认证过滤器
 

需要自定义一个过滤器,这个过滤器会去获取请求头中的token,对token进行解析取出其中的userid。使用userid去redis中获取对应的MyUser对象。然后封装Authentication对象存入SecurityContextHolder。代码如下:

JwtAuthenticationTokenFilter自定义认证过滤器代码如下:
@Componentpublic 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中获取用户信息,如果没有该用户就代表该用户没有登录过        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类中配置自定义过滤器,代码如下:

@Configurationpublic class SecurityConfiguration extends WebSecurityConfigurerAdapter {    @Autowired    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; //自定义过滤器放在UsernamePasswordAuthenticationFilter过滤器之前            http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);    }}

2.4.5 接口测试

在访问/user/login接口之前,其余的接口都不能访问到,然后先登录访问/user/login接口,测试结果如下图:

然后携带请求头token访问其它方法就可以正常的进行访问了,由此可见我们的代码测试验证成功。

至此该篇文章SpringSecurity认证内容结束,下一篇文章作者将继续写SpringSecurity授权内容,未完待续。


来源地址:https://blog.csdn.net/qq_43649937/article/details/131168847

--结束END--

本文标题: SpringSecurity实现前后端分离登录token认证详解

本文链接: https://lsjlt.com/news/385709.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • SpringSecurity实现前后端分离登录token认证详解
    目录 1. SpringSecurity概述 1.1 权限框架 1.1.1 Apache Shiro 1.1.2 SpringSecurity 1.1.3 权限框架的选择 1.2 授权和认证 1.3 SpringSecurity的功能 ...
    99+
    2023-08-31
    spring java spring boot
  • SpringSecurity实现前后端分离的示例详解
    目录1. 认证信息改成JSON格式1.1 新建JsonUsernamePasswordAuthenticationFilter1.2 新建JsonUsernamePasswordLo...
    99+
    2023-03-14
    SpringSecurity前后端分离 SpringSecurity分离
  • SpringBoot+Vue+JWT的前后端分离登录认证详细步骤
    前后端分离的概念在现在很火,最近也学习了一下前后端分离的登录认证。 创建后端springboot工程 这个很简单了,按照idea的一步一步创建就行 文件目录结构: pom文件依赖导...
    99+
    2024-04-02
  • SpringSecurity如何实现前后端分离
    这篇文章主要介绍“SpringSecurity如何实现前后端分离”,在日常操作中,相信很多人在SpringSecurity如何实现前后端分离问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringSecur...
    99+
    2023-07-05
  • SpringSecurity怎么实现前后端分离
    今天小编给大家分享一下SpringSecurity怎么实现前后端分离的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Sprin...
    99+
    2023-07-05
  • Sa-Token实现分布式登录鉴权(Redis集成 前后端分离)
    文章目录 1. Sa-Token 介绍2. 登录认证2.1 登录与注销2.2 会话查询2.3 Token 查询 3. 权限认证3.1 获取当前账号权限码集合3.2 权限校验3.3 角色校...
    99+
    2023-09-02
    分布式 java 服务器
  • PHP实现JWT的Token登录认证
    1、JWT简介 JSON Web Token(缩写 JWT),是目前最流行的跨域认证解决方案。 session登录认证方案:用户从客户端传递用户名、密码等信息,服务端认证后将信息存储在session中,将session_id放到cookie...
    99+
    2017-03-29
    PHP实现Token登录认证 PHP Token认证 Token登录认证
  • Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码
    目录前言本文主要的功能 一、准备工作1、统一错误码枚举2、统一json返回体3、返回体构造工具4、pom5、配置文件二、数据库表设计初始化表数据语句三、Spring Sec...
    99+
    2024-04-02
  • Java SpringSecurity+JWT如何实现登录认证
    这篇文章主要介绍了Java SpringSecurity+JWT如何实现登录认证的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java SpringSecurity+JWT如何实现登录认证文...
    99+
    2023-07-02
  • Vue前端登录token信息验证功能实现
    用户在首次访问网站时,应在登录页面填写账号密码,前端携带用户信息向服务器请求。 1、服务器验证用户信息 验证失败:给前端响应数据 验证通过:对该用户创建token,并以响应数据返回给...
    99+
    2022-12-27
    Vue token验证 Vue前端token验证
  • Vue项目中token验证登录(前端部分)
    本文实例为大家分享了Vue项目中token验证登录的具体代码,供大家参考,具体内容如下 1、前言 最近在做毕业设计,我是做后端的,前端并不是很懂,看vue这个框架看了近两个礼拜才有点...
    99+
    2024-04-02
  • SpringBoot使用Sa-Token实现登录认证
    目录一、设计思路二、登录与注销三、会话查询四、Token 查询五、来个小测试,加深一下理解一、设计思路 对于一些登录之后才能访问的接口(例如:查询我的账号资料),我们通常的做法是增加...
    99+
    2023-05-14
    SpringBoot Sa-Token登录认证 SpringBoot 登录认证
  • node+vue前后端分离实现登录时使用图片验证码功能
    目录后端代码前端代码获取验证码方法登录验证方法记录一下前端使用验证码登录的过程后端用的是node.js,关键模块是svg-captcha前端使用的是vue2最后的登录界面如下: 后...
    99+
    2022-11-13
     vue图片验证码登录 node前后端分离验证码登录
  • vue前后端分离如何实现单点登录跨域
    这篇文章主要介绍“vue前后端分离如何实现单点登录跨域”,在日常操作中,相信很多人在vue前后端分离如何实现单点登录跨域问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue前后端分离如何实现单点登录跨域”的疑...
    99+
    2023-07-04
  • SpringBoot前后端分离实现验证码操作
    目录1.SpringBoot版本2.引入依赖3.实现思路新建验证码枚举类定义验证码配置信息定义验证逻辑生成类在控制层上定义验证码生成接口效果体验在前端调用接口1.SpringBoot...
    99+
    2024-04-02
  • thinkphp怎么实现前后端分离验证码
    这篇文章主要介绍了thinkphp怎么实现前后端分离验证码的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇thinkphp怎么实现前后端分离验证码文章都会有所收获,下面我们一起来看看吧。一、验证码的作用在互联网时...
    99+
    2023-07-06
  • Java后端登录实现返回token
    前言 最近工作中需要等待前端进行联调和测试,互联网都知道,当到了联调和提测的时候,基本上的工作都是一阵一阵,中间是有很多空隙时间的,于是为了度过这些空隙时间,写几篇博客,记录一下 处...
    99+
    2024-04-02
  • PHP如何实现JWT的Token登录认证
    本篇内容介绍了“PHP如何实现JWT的Token登录认证”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、JWT简介JSON Web Tok...
    99+
    2023-06-21
  • Django实现前后端登录
    目录前端登录1. login.vue2.设置路由3.登录标签设置后端登录2.1 创建一个用户表2.2 Settings/dev/py 新增配置2.3 运行时报错修& 改配置环...
    99+
    2024-04-02
  • SpringBoot怎么使用Sa-Token实现登录认证
    这篇文章主要讲解了“SpringBoot怎么使用Sa-Token实现登录认证”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringBoot怎么使用Sa-Token实现登录认证”吧!一、设...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作