前后端分离开发下的权限管控 :SpringSecurity 框架

首先在了解前后端分离模式下使用SpringSecurity框架之前,我们需要先了解token和jwt(Json Web Token)技术

token 和 session 的区别?

由于http协议是无状态的协议,一次请求之后浏览器端无法保存服务器端携带过来的数据,所以为了解决这一问题,所以就有session 的出现来保存本次会话状态下的数据。(session底层的实现是基于cookie来实现的,每次请求通过携带 Jsessionid来获取Session域中的数据)
SESSION 是服务器通过 Key-Value 对来保存数据的一种机制,比如 APP 的登录状态可以用 SESSION 来保存。

TOKEN 翻译过来叫令牌,令牌是什么意思?可以拿现实中的令牌对比,现实中的令牌起到通行证的作用,而这在服务端也是一样的。我们在登录后,服务端使用 SESSION 保存我们的登录状态,并把 SESSION 的 Key 返回给客户端,那么这个 Key 就成为我们的令牌(TOKEN),我们以后再访问数据,就直接把这个 TOKEN 随着请求一起发送给服务端,这样服务端通过这个 TOKEN 在 SESSION 中查找数据,如果有就说明 TOKEN 有效(就像你去旅游,关口认可你的通行证),并取出你的登录数据,利用你的用户信息(保存在登录数据内)查出你想要的内容

session的缺点:
1、服务器压力增大

通常session是存储在内存中的,每个用户通过认证之后都会将session数据保存在服务器的内存中,而当用户量增大时,服务器的压力增大。

2、CSRF跨站伪造请求攻击

session是基于cookie进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

3、扩展性不强

如果将来搭建了多个服务器,虽然每个服务器都执行的是同样的业务逻辑,但是session数据是保存在内存中的

(不是共享的),用户第一次访问的是服务器1,当用户再次请求时可能访问的是另外一台服务器2,

服务器2获取不到session信息,就判定用户没有登陆过。
token 的实现原理
token其实就是在服务器端生成的令牌,当用户发起登录请求之后”/login“,用户登录成功之后

,服务器端会拿到该登录用户的 用户名 以及 密码,使用 JWT技术来生成 一个 token令牌

(其实就是一串加密的字符串),并且响应给前端。前端将该 token 令牌保存到  sessionStorage 

或者 localStorage 中,前端每一次请求的时候都会携带上该 token令牌,

服务器端接收到请求判断 本次请求是否携带 token,来进行放行本次请求,起到一个 验名身份的效果。

不用每次请求 都去 session 中判断是否有当前用户来验明自己的身份。token如果存储在硬盘上的话,就说明该令牌永

久有效。
JWT 技术
JWT 其实就是用来生成 token 的技术,传入对应的用户信息,根据传入的用户信息来生成对应的token密钥 , jwt通常有三部分组成:头部信息(header) 消息体, 和签名

头部信息:指定了该JWT使用的签名算法

消息体:包含JWT中储存的信息

签名:通过密钥跟算法生成签名
搞清楚了 token 和 jwt 之后 我们来具体的谈谈 前后端分离模式下 如何完成权限的验证。

SpringSecurity 其实本质上就是一堆的 过滤器链来实现的, 每次请求都会进入对应的过滤器来完成 认证和 授权等。

难点 ::: SpringSecurity在接受前端 发起的 登录请求时,无法接受 json格式的数据,只能接收到 form表单提交过来的数据,也就是 url?name=value&name=value这种数据格式。所以前端传递json格式的数据发起请求时,SpringSecurity无法接收所以我们要进行重写 对应的 用户名密码过滤器类。UsernamePasswordAuthenticationFilter , 搞清楚这一点之后,我们来研究以下SpringSecurity的整个执行流程。

在这里插入图片描述
springSecurity的 携带 token的 认证 流程
在这里插入图片描述

开始配置

(1) 引入对应的maven依赖

<dependencies>
        <!-- jwt 相关依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
        <!--导入WebMVC的依赖,不要写版本号是因为spring底层已经默认配置了-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--导入mybatis的场景启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

        <!--引入Jackson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>


        <!--log4j启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
            <version>1.3.8.RELEASE</version>
        </dependency>

        <!--JDBC连接驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--引入driud启动器-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>


        <!--解决生成的token存储 -->
        <dependency>
            <groupId>com.diffplug.guava</groupId>
            <artifactId>guava-cache</artifactId>
            <version>19.0.0</version>
        </dependency>



    </dependencies>

(2) 自定义的 UserDetails 实体类 来供 springSecurity 传递用户信息给 对应的登录成功的过滤器

package org.jcgl.service.auth;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Collection;

@Component
public class MyUserDetails implements UserDetails {
   
    private String username;

    private String password;

    private Integer state;

    private Collection<? extends GrantedAuthority> authorities;

    public MyUserDetails() {
   
    }

    public MyUserDetails(String username, String password, Integer state, Collection<? extends GrantedAuthority> authorities) {
   
        this.username = username;
        this.password = password;
        this.state = state;
        this.authorities = authorities;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
   
        return authorities;
    }

    @Override
    public String getPassword() {
   
        return password;
    }

    @Override
    public String getUsername() {
   
        return username;
    }

    // 账户是否未过期
    @Override
    public boolean isAccountNonExpired() {
   
        return true;
    }

    // 账户是否未被锁
    @Override
    public boolean isAccountNonLocked() {
   
        return true;
    }

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

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

    public Integer getState() {
   
        return state;
    }

    public void setState(Integer state) {
   
        this.state = state;
    }

    @Override
    public String toString() {
   
        return "JwtUser{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", state=" + state +
                ", authorities=" + authorities +
                '}';
    }

}

(3)自定义对应的UserDetailsService 实现类 来进行 登录用户的具体认证 信息

package org.jcgl.service.auth;

import org.jcgl.entity.UserEntity;
import org.jcgl.service.RoleService;
import org.jcgl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class UserDetailsServiceImpl implements UserDetailsService {
   

    @Autowired
    @Qualifier("userServiceImpl")
    private UserService userService;

    @Autowired
    @Qualifier("roleServiceImpl")
    private RoleService roleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
   

        System.out.println("自定义用户名密码过滤器验证成功,生成token进入自定义的UserDetailsService,前端传入的用户名为==============>" + username);

        // 根据传入的用户名查询出对应的用户
        UserEntity user = userService.getUserByUserName(username);
        System.out.println("根据用户名查询出用户信息为:" + user);
        if (user == null) {
   
            System.out.println("根据用户名查询用户为空");
            //用户密码错误抛出异常
            throw new UsernameNotFoundException(String.format("%s.这个用户不存在", username));
        } else {
   
            System.out.println("根据用户名查询用户 不 为空,开始根据用户名查询出对应的角色!!!");

            //用户名存在,根据用户名查找出对应角色,该方法默认为 null 还未具体实现!
            List<String> roles = roleService.getRolesByUserName(username);

            // 创建对应的角色集合用来存储该用户对应的角色
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();

            for (String role : roles) {
   
                // 将查询相出的角色 封装到SimpleGrantedAuthority对象中
                // 并且存入 List<SimpleGrantedAuthority> 集合中
                authorities.add(new SimpleGrantedAuthority(role));
            }

            System.out.println("loadUserByUsername......user ===> " + user);
            //将查询出来的用户名和,对应的密码,状态码和权限存入 自定义的 UserDetails对象中并返回
            // 返回的数据 将被 成功之后的处理器调用 successHandler
            return new MyUserDetails(user.getUsername(), user.getPassword(), user.getDelState(), authorities);
        }
    }
}

(3)自定义请求的前置拦截器,任何请求都会先进入该拦截器,判断本次请求是否携带 token 以及 处理 token是过期等问题。

package org.jcgl.security.myComponent;

import org.jcgl.security.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值