SpringSecurity简单入门

SpringSecurity简单入门

1、SpringSecurity简介

Spring SecuritySpring 家族中的一个安全管理框架,相比于另外一个安全框架 Shiro,它提供了更丰富

的功能,社区资源也比 Shiro 丰富。

一般来说中大型的项目都是使用 SpringSecurity 来做安全框架,小项目使用 Shiro 的比较多,因为相比于

SpringSecurityShiro 上手更加的简单。

一般 Web 应用需要进行认证和授权:

  • 认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户。

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

认证和授权也是 SpringSecurity 作为安全框架的核心功能。

2、快速入门

2.1 新建测试控制器

package com.example.springsecuritydemo1.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author test
 */
@RestController
public class HelloWorldController {
   

    @GetMapping("hello/world")
    public String helloWorld() {
   
        return "hello world!";
    }
}

2.2 pom依赖

SpringBoot 项目中使用 SpringSecurity 我们只需要引入依赖即可实现入门案例。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>SpringSecurityDemo1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringSecurityDemo1</name>
    <description>SpringSecurityDemo1</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

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

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

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.3 使用

引入依赖后我们在尝试去访问接口就会自动跳转到一个 SpringSecurity 的默认登陆页面,默认用户名是

user,密码会输出在控制台,必须登陆之后才能对接口进行访问。

# 控制台输出的密码
Using generated security password: 83cf6d36-d553-4c52-bd4b-f5a107a0a945

我们访问之前的接口需要输入用户名和密码才可以访问:

在这里插入图片描述

访问接口:

在这里插入图片描述

3、认证

3.1 登录校验流程

在这里插入图片描述

3.2 原理初探

想要知道如何实现自己的登陆流程就必须要先知道入门案例中 SpringSecurity 的流程。

3.2.1 SpringSecurity完整流程

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

例中的过滤器。

在这里插入图片描述

图中只展示了核心过滤器,其它的非核心过滤器并没有在图中展示。

  • UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请求,入

    门案例的认证工作主要有它负责。

  • ExceptionTranslationFilter:处理过滤器链中抛出的任何 AccessDeniedException

    AuthenticationException

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

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

在这里插入图片描述

3.2.2 认证流程详解

在这里插入图片描述

概念速查:

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

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

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

    法。

  • UserDetails接口:提供核心用户信息,通过 UserDetailsService 根据用户名获取处理的用户信息要封装

    UserDetails 对象返回,然后将这些信息封装到 Authentication 对象中。

在这里插入图片描述

在这里插入图片描述

3.3 解决问题和思路分析

登录:

①、自定义登录接口

  • 调用 ProviderManager 的方法进行认证,如果认证通过生成 jwt

  • 把用户信息存入 redis

②、自定义 UserDetailsService

  • 在这个实现类中去查询数据库

校验:

①、定义 Jwt 认证过滤器

  • 获取 token
  • 解析 token 获取其中的 userid
  • redis 中获取用户信息
  • 存入 SecurityContextHolder

4、认证案例

4.1 pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>SpringSecurityDemo2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringSecurityDemo2</name>
    <description>SpringSecurityDemo2</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

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

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

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

        <!-- 引入redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- 引入fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

        <!-- 引入jwt依赖 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        
        <!-- 引入lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4.2 Redis相关配置

# Redis配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000ms

4.3 Redis配置类

package com.example.springsecuritydemo2.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author test
 */
@Configuration
public class RedisConfig {
   

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
   
        // 定义Jackson2JsonRedisSerializer序列化对象
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 创建RedisTemplate<String, Object>对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(redisConnectionFactory);
        StringRedisSerializer stringSerial = new StringRedisSerializer();
        // redis key 序列化方式使用stringSerial
        template.setKeySerializer(stringSerial);
        // redis value 序列化方式使用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // redis hash key 序列化方式使用stringSerial
        template.setHashKeySerializer(stringSerial);
        // redis hash value 序列化方式使用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

4.4 Redis工具类

package com.example.springsecuritydemo2.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author test
 */
@Component
public class RedisUtils {
   

    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
   
        this.redisTemplate = redisTemplate;
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
   
        try {
   
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
   
            return false;
        }
    }

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
   
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值或多个
     */
    public void del(String... key) {
   
        if (key != null && key.length > 0) {
   
            if (key.length == 1) {
   
                redisTemplate.delete(key[0]);
            } else {
   
                redisTemplate.delete(Arrays.asList(key));
            }
        }
    }
}

4.5 Jwt工具类

package com.example.springsecuritydemo2.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author test
 */
@Component
public class JwtTokenUtil {
   

    /**
     * 荷载claim的名称
     */
    private static final String CLAIM_KEY_USERNAME = "sub";

    /**
     * 荷载的创建时间
     */
    private static final String CLAIM_KEY_CREATED = "created";

    /**
     * jwt令牌的秘钥
     */
    private final String secret = "yeb-secret";

    /**
     * jwt的实效时间
     */
    private final Long expiration = 604800L;

    /**
     * 根据用户信息生成token
     *
     * @param username 用户名
     * @return String
     */
    public String generateToken(String username) {
   
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, username);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 根据荷载生成JWTToken
     *
     * @param claims 生成token的信息
     * @return String
     */
    private String generateToken(Map<String, Object> claims) {
   
        return Jwts.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值