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

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

目录

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的特点:

1. 与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)基于角色的访问控制,通过抽象出“用户、角色、权限”"三个概念,实现用户分配角色,角色分配权限的权限管理方式,也是目前企业中权限管理主要实现方案。

案例如下图所示:

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

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

导入依赖

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

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

2.2 认证

2.2.1 登录校验流程

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

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

 2.2.2 SpringSecurity完整流程

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

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

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

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

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


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

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

 2.2.3 认证流程详解

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

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

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

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

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

2.3 思路分析

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

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

2.4 代码实战

2.4.1  自定义UserDetailsService类

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

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

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

@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);
        //查询不到该用户信息抛异常
        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。

@Configuration
public 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代码如下:

@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()//关闭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自定义认证过滤器代码如下:
@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中获取用户信息,如果没有该用户就代表该用户没有登录过
        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接口,测试结果如下图:

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

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

至此该篇文章SpringSecurity认证内容结束,下一篇文章作者将继续写SpringSecurity授权内容,未完待续。文章来源地址https://www.toymoban.com/news/detail-480448.html


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

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

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

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

相关文章

  • SpringSecurity6+JWT实现前后端分离

    SpringSecurity6+JWT实现前后端分离

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

    2024年02月22日
    浏览(13)
  • 实现前后端分离的登陆验证token思路

    在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下: 1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码 2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token 3、前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面

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

    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日
    浏览(14)
  • 若依前后端分离版:增加新的登录接口,用于小程序或者APP获取token,并使用若依的验证方法

    若依前后端分离版:增加新的登录接口,用于小程序或者APP获取token,并使用若依的验证方法

    登录校验 ——AppLoginService类  具体代码  具体代码  具体代码 此时运行时,会有冲突!!! 需要在 xxx-framework/src/main/java/....../SecurityConfig中条件 如图:  此时启动项目不会报冲突的错 千万千万要添加!!! 下图中的LongUser类要添加东西  要在public String getPassword(){}中添加

    2024年02月07日
    浏览(50)
  • 若依@v3.8.6前后端分离版:添加新用户表添加新登录接口,用于小程序或者APP获取token,并使用若依的验证方法

    若依@v3.8.6前后端分离版:添加新用户表添加新登录接口,用于小程序或者APP获取token,并使用若依的验证方法

    1、创建一个前端APP用户表:app_user表,并插入一条数据 2、创建 AppUser 实体类 目录:ruoyi-common——src——main——java——com.ruoyi.common——core——domian——entity 注意:与 SysUser 同级目录 3、创建 AppUserMapper.xml 映射文件 目录:ruoyi-system——src——main——resources——mapper.system 注

    2024年02月04日
    浏览(51)
  • Springboot+redis实现token的登录认证

    Springboot+redis实现token的登录认证

    1.在用户登录后,如果要访问其他路径下的资源的话,我们是否还需要再验证一遍呢?而且我们登陆上系统长时间不操作的话还需不需要再次验证?所以这种情况下就很需要token来实现登录功能。并通过redis(redis是一个key-value存储系统,它支持存储的value类型相对更多,包括

    2023年04月09日
    浏览(7)
  • springBoot JWT实现websocket的token登录拦截认证

    功能:所有关于websocket的请求必须登录,实现websocket需要登录后才可使用,不登录不能建立连接。 后台spring security配置添加websocket的请求可以匿名访问,关于websocket的请求不要认证就可以随意访问,去除匿名访问后,前端在与websocket建立链接无法在请求头里直接加入Authoriz

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

    若依前后端分离项目集成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日
    浏览(52)
  • springboot整合Sa-Token实现登录认证和权限校验(万字长文)

    springboot整合Sa-Token实现登录认证和权限校验(万字长文)

    目前在国内的后端开发中,常用的安全框架有spring security、shiro。现在,介绍一款由国人开发的安全框架Sa-Token。这个框架完全由国人开发,所提供的Api文档和一些设置都是比较符合国人的开发习惯的,本次就来介绍一下如何在spring boot框架中整合Sa-Token框架,以实现我们最常

    2024年04月27日
    浏览(13)
  • 一文教会你SpringSecurity 自定义认证登录

    一文教会你SpringSecurity 自定义认证登录

    现在登录方式越来越多,传统的账号密码登录已经不能满足我们的需求。可能我们还需要手机验证码登录,邮箱验证码登录,一键登录等。这时候就需要我们自定义我们系统的认证登录流程,下面,我就一步一步在SpringSecurity 自定义认证登录,以手机验证码登录为例 Spring S

    2024年04月13日
    浏览(13)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包