SpringBoot整合SpringSecurity+Redis权限控制


1、认识SpringSecurity

Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
记住几个类:

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。


2、效果截图

2.1、登录接口

登录效果图

2.2、注册接口

注册接口
数据库

2.3、管理员权限接口

管理员权限接口

2.4、普通用户权限接口

普通用户权限接口

2.5、公共接口接口

公共权限接口

2.6、Redis缓存效果

Redis缓存效果


3、前期准备工作

3.1、导入相关依赖

 		<!-- 配置使用redis启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--            <scope>runtime</scope>-->
        </dependency>

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

3.2、创建数据库

创建用户表sys_user:

CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建权限表sys_role:

CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建用户-角色表sys_user_role:

CREATE TABLE `sys_user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `fk_role_id` (`role_id`),
  CONSTRAINT `fk_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

初始化一下数据:

INSERT INTO `sys_role` VALUES (1, 'ROLE_admin');
INSERT INTO `sys_role` VALUES (2, 'ROLE_user');

INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$LiJV/NxnZK2fiUemUthYCeazPXV/RzW5KQWhz5CZdYCbsUHxcRZWK');
INSERT INTO `sys_user` VALUES (2, 'user', '$2a$10$LiJV/NxnZK2fiUemUthYCeazPXV/RzW5KQWhz5CZdYCbsUHxcRZWK');

INSERT INTO `sys_user_role` VALUES (1, 1);
INSERT INTO `sys_user_role` VALUES (2, 2);

数据库中的权限格式为ROLE_XXX,是Spring Security规定的(后面会讲其他方式)。


4、核心逻辑

核心逻辑

5、项目结构

项目结构


6、代码

6.1、Entity实体类

package com.yuange.demo.entity;

import java.io.Serializable;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:09
 */
public class SysRole implements Serializable {
    static final long serialVersionUID = 1L;

    private Integer id;

    private String name;

    // 省略getter/setter

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


package com.yuange.demo.entity;

import java.io.Serializable;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:08
 */
public class SysUser implements Serializable {
    static final long serialVersionUID = 1L;

    private Integer id;

    private String name;

    private String password;

    // 省略getter/setter

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


package com.yuange.demo.entity;

import java.io.Serializable;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:09
 */
public class SysUserRole implements Serializable {
    static final long serialVersionUID = 1L;

    private Integer userId;

    private Integer roleId;

    // 省略getter/setter

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }
}


6.2、Utils工具类

package com.yuange.demo.util;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * @author lichangyuan
 * @create 2021-03-18 14:43
 */
public class CustomUtils {
    /**
     * 响应json数据给前端
     *
     * @param response
     * @param obj
     */
    public static void sendJsonMessage(HttpServletResponse response, Object obj) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            response.setContentType("application/json; charset=utf-8");
            PrintWriter writer = response.getWriter();
            //Java对象转为Json格式的数据(objectMapper.writeValueAsString)
            writer.print(objectMapper.writeValueAsString(obj));
            writer.close();
            //内容写到客户端浏览器
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

package com.yuange.demo.util;


import java.io.Serializable;

public class JsonData implements Serializable {

    /**
     * 状态码 0表示成功过,-1,-2,-3、、、为失败
     */
    private Integer code;

    /**
     * 业务数据
     */
    private Object data;

    /**
     * 信息表示
     */
    private String msg;

    public JsonData() {
    }

    public JsonData(Integer code, Object data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }


    /**
     * 成功,不用返回数据
     *
     * @return
     */
    public static JsonData buildSuccess() {
        return new JsonData(0, null, null);
    }

    /**
     * 成功,返回数据
     *
     * @param data
     * @return
     */
    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data, null);
    }


    /**
     * 失败,固定状态码
     *
     * @param msg
     * @return
     */
    public static JsonData buildError(String msg) {
        return new JsonData(-1, null, msg);
    }


    /**
     * 失败,自定义错误码和信息
     *
     * @param code
     * @param msg
     * @return
     */
    public static JsonData buildError(Integer code, String msg) {
        return new JsonData(code, null, msg);
    }


    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

package com.yuange.demo.util;

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

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
@author lichangyuan
@create 2021-03-19 12:25
*/

@Component
public final class RedisUtil {

    //注入自己写的redisTemplate
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================
    /**
     * 指定缓存失效时间
     * @param key  键
     * @param time 时间(秒)
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }


    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


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


    // ============================String=============================

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

    /**
     * 普通缓存放入
     * @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) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */

    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 递增
     * @param key   键
     * @param delta 要增加几(大于0)
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }


    /**
     * 递减
     * @param key   键
     * @param delta 要减少几(小于0)
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }


    // ================================Map=================================

    /**
     * HashGet
     * @param key  键 不能为null
     * @param item 项 不能为null
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * HashSet 并设置时间
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }


    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }


    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }


    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }


    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     * @param key 键
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 获取set缓存的长度
     *
     * @param key 键
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */

    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取list缓存的长度
     *
     * @param key 键
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */

    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */

    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }

    }

}

6.3、Handler层

注销处理器

package com.yuange.demo.handler;

import com.yuange.demo.util.CustomUtils;
import com.yuange.demo.util.JsonData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 注销处理器
 *
 * @author lichangyuan
 * @create 2021-03-18 14:16
 */
@Component
public class AuthenticationLogout implements LogoutSuccessHandler {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        String token = request.getHeader("token");
        if (token == null) {
            token = request.getParameter("token");
        }
        try {
            if (token == null) {
                //token为空表示未登录,注销失败
                CustomUtils.sendJsonMessage(response, JsonData.buildError("未登录,不能进行注销操作!!!"));
            } else {
                String username = stringRedisTemplate.opsForValue().get(token);
                if (username == null) {
                    //token不正确,注销失败
                    CustomUtils.sendJsonMessage(response, JsonData.buildError("登录凭证异常,注销失败!!!"));
                } else {
                    //token正确,注销成功
                    CustomUtils.sendJsonMessage(response, JsonData.buildError("注销成功"));
                    //清空token
                    stringRedisTemplate.delete(token);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        CustomUtils.sendJsonMessage(response, JsonData.buildError("登录过期,重新登录"));
    }

}

未登录时处理器

package com.yuange.demo.handler;

/**
 * 未登录时处理器
 * @author lichangyuan
 * @create 2021-03-18 14:41
 */

import com.yuange.demo.util.CustomUtils;
import com.yuange.demo.util.JsonData;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 未登录时处理器
 */
public class TokenAuthenticationEntryPoint implements AuthenticationEntryPoint {


    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        CustomUtils.sendJsonMessage(response, JsonData.buildError("请登录!!"));
    }
}

权限不足处理器

package com.yuange.demo.handler;

import com.yuange.demo.util.CustomUtils;
import com.yuange.demo.util.JsonData;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;


import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 权限不足处理器
 */
public class TokenAccessDeniedHandler implements AccessDeniedHandler {


    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        CustomUtils.sendJsonMessage(response, JsonData.buildError("权限不够,请联系管理员!!!"));
    }


}

6.4、自定义Filter

请求过滤器 , token没有或者不正确的时候, 告诉用户执行相应操作,token正确且未认真的情况下则放行请求, 交由认证过滤器进行认证操作

package com.yuange.demo.filter;

/**
 * @author lichangyuan
 * @create 2021-03-18 14:45
 */


import com.yuange.demo.service.impl.UserServiceImpl;
import com.yuange.demo.util.CustomUtils;
import com.yuange.demo.util.JsonData;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义请求过滤器,token没有或者不正确的时候,
 * 告诉用户执行相应操作,token正确且未认真的情况下则放行请求,
 * 交由认证过滤器进行认证操作
 */
public class OncePerRequestAuthoricationFilter extends BasicAuthenticationFilter {

    StringRedisTemplate stringRedisTemplate;

    UserServiceImpl userServiceImpl;

    public OncePerRequestAuthoricationFilter(AuthenticationManager authenticationManager, StringRedisTemplate stringRedisTemplate, UserServiceImpl userServiceImpl) {
        super(authenticationManager);
        this.stringRedisTemplate=stringRedisTemplate;
        this.userServiceImpl=userServiceImpl;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String token=request.getHeader("token");
        if(token==null || token.equals("")){
            //token为空,则返回空
            chain.doFilter(request, response);
        }
        String username=stringRedisTemplate.opsForValue().get(token);
        try{
            //判断token情况,给予对应的处理方案
            if(username==null){
                CustomUtils.sendJsonMessage(response, JsonData.buildError("登录凭证不正确或者超时了,请重新登录!!!"));
            }else{
                UserDetails userDetails = userServiceImpl.loadUserByUsername(username);
                if(userDetails!=null){
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(username,null,userDetails.getAuthorities());
                    response.setHeader("token",token);
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
            }
        }catch (Exception e){
            CustomUtils.sendJsonMessage(response, JsonData.buildError("登录凭证异常!!!"));
        }
        super.doFilterInternal(request,response,chain);
    }
}

认证过滤器, 判断认证成功还是失败,并给予相对应的逻辑处理

package com.yuange.demo.filter;

/**
 * @author lichangyuan
 * @create 2021-03-18 14:56
 */

import com.yuange.demo.entity.SysUser;
import com.yuange.demo.mapper.SysUserMapper;
import com.yuange.demo.util.CustomUtils;
import com.yuange.demo.util.JsonData;
import com.yuange.demo.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 自定义认证过滤器,判断认证成功还是失败,并给予相对应的逻辑处理
 */
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {


    AuthenticationManager authenticationManager;

    StringRedisTemplate stringRedisTemplate;


    public AuthenticationFilter(AuthenticationManager authenticationManager, StringRedisTemplate stringRedisTemplate) {
        this.authenticationManager = authenticationManager;
        this.stringRedisTemplate = stringRedisTemplate;
    }



    //未认证时调用此方法,判断认证是否成功,认证成功与否由authenticationManager.authenticate()去判断,我们在这里只负责传递所需要的参数即可
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password, new ArrayList<>()));
    }

    //验证成功操作
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        /**
         * 验证成功则向redis缓存写入token,然后在响应头添加token,并向前端返回
         */
        String token = UUID.randomUUID().toString().replaceAll("-", "");  //token本质就是随机生成的字符串
        //由于不能使用@Autowired因此使用stringRedisTemplate
        stringRedisTemplate.opsForValue().set(token, request.getParameter("username"), 60 * 10, TimeUnit.SECONDS);    //存入缓存中
        response.setHeader("token", token);  //在响应头添加token
        CustomUtils.sendJsonMessage(response, JsonData.buildSuccess(token));
    }

    //验证失败
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        /**
         * 验证成功则向前端返回失败原因
         */
        CustomUtils.sendJsonMessage(response, JsonData.buildError("账号或者密码错误"));
    }
}

6.5、自定义Config

springsecurity核心配置文件,无论是处理器还是过滤器都需要注入到此

package com.yuange.demo.config;

import com.yuange.demo.filter.AuthenticationFilter;
import com.yuange.demo.filter.OncePerRequestAuthoricationFilter;
import com.yuange.demo.handler.AuthenticationLogout;
import com.yuange.demo.handler.TokenAccessDeniedHandler;
import com.yuange.demo.handler.TokenAuthenticationEntryPoint;
import com.yuange.demo.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;


/**
 * @author lichangyuan
 * @create 2021-03-18 15:56
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //java操作redis的string类型数据的类
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    //注销处理器
    @Autowired
    AuthenticationLogout authenticationLogout;

    //加密
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public UserDetailsService userDetailsService() {
        return new UserServiceImpl();
    }


    /**
     * 认证
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(bCryptPasswordEncoder());
    }


    /**
     * 授权
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //权限管理
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")

                .and()
                //开启跨域访问
                .cors().and().
                //关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
                csrf().disable()
                .authorizeRequests()
                //任何请求方式
                .anyRequest().permitAll()

                .and()
                .logout()
                .permitAll()
                .logoutSuccessHandler(authenticationLogout) //注销时的逻辑处理

                .and()
                .addFilter(new AuthenticationFilter(authenticationManager(), stringRedisTemplate))   //自定义认证过滤器
                .addFilter(new OncePerRequestAuthoricationFilter(authenticationManager(), stringRedisTemplate, (UserServiceImpl) userDetailsService())) //自定义请求过滤器
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)     //去除默认的session、cookie

                .and()
                .exceptionHandling().authenticationEntryPoint(new TokenAuthenticationEntryPoint())  //未登录时的逻辑处理
                .accessDeniedHandler(new TokenAccessDeniedHandler());    //权限不足时的逻辑处理
    }


    /**
     * 用于解决跨域问题
     *
     * @return
     */
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }
}

Redis相应的配置

package com.yuange.demo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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 lichangyuan
 * @create 2021-03-19 12:24
 */
@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        //先改成<String, Object>类型
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        //Json序列化配置
        //1、json解析任意的对象(Object),变成json序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //用ObjectMapper进行转义
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //该方法是指定序列化输入的类型,就是将数据库里的数据按照一定类型存储到redis缓存中。
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //2、String的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
}

6.6、Service逻辑层

UserServiceImpl.java, 实现UserDetailsService里面的loadUserByUsername()方法,AuthenticationManager会调用此方法去获取用户数据信息,从而完成认证。

package com.yuange.demo.service.impl;

import com.yuange.demo.entity.SysRole;
import com.yuange.demo.entity.SysUser;
import com.yuange.demo.entity.SysUserRole;
import com.yuange.demo.service.SysRoleService;
import com.yuange.demo.service.SysUserRoleService;
import com.yuange.demo.service.SysUserService;
import com.yuange.demo.service.UserService;
import com.yuange.demo.util.JsonData;
import com.yuange.demo.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

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

/**
 * @author lichangyuan
 * @create 2021-03-18 14:49
 */
@Service
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    SysUserService sysUserService;

    @Autowired
    SysRoleService sysRoleService;

    @Autowired
    SysUserRoleService sysUserRoleService;

    @Autowired
    RedisUtil redisUtil;


    /**
     * 实现UserDetailsService接口的方法,用于获取用户个人信息
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //根据用户名查找用户,
        SysUser user = sysUserService.selectByName(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户名错误!!");
        }

        //获取用户权限,并把其添加到GrantedAuthority中
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

        // 添加权限
        List<SysUserRole> userRoles = sysUserRoleService.listByUserId(user.getId());
        for (SysUserRole userRole : userRoles) {
            SysRole role = sysRoleService.selectById(userRole.getRoleId());
            grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        //用户名,密码,权限
        return new User(username, user.getPassword(), grantedAuthorities);
    }

    /**
     * 注册操作
     *
     * @param user
     * @return
     */
    public JsonData register(SysUser user) {
        user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));  //对密码进行加密
        int insert = sysUserService.insert(user);
        if (insert > 0) {
            return JsonData.buildSuccess("注册成功!");
        } else {
            return JsonData.buildError("注册失败!");
        }
    }
}

package com.yuange.demo.service;

/**
 * @author lichangyuan
 * @create 2021-03-18 14:50
 */
public interface UserService {
}

package com.yuange.demo.service;

import com.yuange.demo.entity.SysUser;
import com.yuange.demo.mapper.SysUserMapper;
import com.yuange.demo.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:10
 */
@Service
public class SysUserService {
    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    RedisUtil redisUtil;

    public SysUser selectById(Integer id) {
        SysUser user = (SysUser) redisUtil.hget("SysUserService","selectById"+id);
        if(user==null){
            user=userMapper.selectById(id);
            redisUtil.hset("SysUserService","selectById" + id, user, 180);
        }
        return user;
    }

    public SysUser selectByName(String name) {
        SysUser user = (SysUser) redisUtil.hget("SysUserService","selectByName"+name);
        if(user==null){
            user=userMapper.selectByName(name);
            redisUtil.hset("SysUserService","selectByName" + name, user, 180);
        }
        return user;
    }

    public Integer insert(SysUser sysUser){
        redisUtil.del("SysUserService");
        return userMapper.insert(sysUser);
    }
}


package com.yuange.demo.service;

import com.yuange.demo.entity.SysRole;
import com.yuange.demo.entity.SysUserRole;
import com.yuange.demo.mapper.SysUserRoleMapper;
import com.yuange.demo.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:11
 */
@Service
public class SysUserRoleService {
    @Autowired
    private SysUserRoleMapper userRoleMapper;

    @Autowired
    RedisUtil redisUtil;

    public List<SysUserRole> listByUserId(Integer userId) {
        List<SysUserRole> sysUserRoleList = (List<SysUserRole>) redisUtil.hget("SysUserRoleService","listByUserId" + userId);
        if (sysUserRoleList == null) {
            sysUserRoleList = userRoleMapper.listByUserId(userId);
            redisUtil.hset("SysUserRoleService","listByUserId" + userId, sysUserRoleList, 180);
        }
        return sysUserRoleList;
    }
}


package com.yuange.demo.service;

import com.yuange.demo.entity.SysRole;
import com.yuange.demo.mapper.SysRoleMapper;
import com.yuange.demo.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:11
 */
@Service
public class SysRoleService {
    @Autowired
    private SysRoleMapper roleMapper;

    @Autowired
    RedisUtil redisUtil;

    public SysRole selectById(Integer id) {
        SysRole sysRole = (SysRole) redisUtil.hget("SysRoleService","selectById" + id);
        if (sysRole == null) {
            sysRole = roleMapper.selectById(id);
            redisUtil.hset("SysRoleService","selectById" + id, sysRole, 180);
        }
        return sysRole;
    }
}


6.7、Mapper持久层

package com.yuange.demo.mapper;

import com.yuange.demo.entity.SysRole;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:10
 */
@Mapper
public interface SysRoleMapper {
    @Select("SELECT * FROM sys_role WHERE id = #{id}")
    SysRole selectById(Integer id);
}


package com.yuange.demo.mapper;

import com.yuange.demo.entity.SysUser;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:09
 */
@Mapper
public interface SysUserMapper {
    @Select("SELECT * FROM sys_user WHERE id = #{id}")
    SysUser selectById(Integer id);

    @Select("SELECT * FROM sys_user WHERE name = #{name}")
    SysUser selectByName(String name);

    @Insert("insert into sys_user values ( null, #{name}, #{password})")
    Integer insert(SysUser sysUser);
}


package com.yuange.demo.mapper;

import com.yuange.demo.entity.SysUserRole;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @author lichangyuan
 * @create 2021-03-16 17:10
 */
@Mapper
public interface SysUserRoleMapper {
    @Select("SELECT * FROM sys_user_role WHERE user_id = #{userId}")
    List<SysUserRole> listByUserId(Integer userId);
}



6.8、Controller控制层

UserController.java , @PreAuthorize(“hasRole(‘ROLE_USER’)”) 指定接口拥有ROLE_USER的权限方可访问的注解。但是我没有使用下面我给注释掉了,我使用了自定义配置路径在SecurityConfig类中(自定义WebSecurityConfigurerAdapter)

package com.yuange.demo.controller;

import com.yuange.demo.entity.SysUser;
import com.yuange.demo.service.impl.UserServiceImpl;
import com.yuange.demo.util.JsonData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Decoder;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author lichangyuan
 * @create 2021-03-18 17:31
 */
@RestController
public class UserController {

    @Autowired
    UserServiceImpl userServiceImpl;

//    @Autowired
//    AuthenticationManager authenticationManager;

    /**
     * 注册操作
     *
     * @param user
     * @return
     */
    @PostMapping("/register")
    public JsonData register(@RequestBody SysUser user) {
        return JsonData.buildSuccess(userServiceImpl.register(user));
    }

    /**
     * 当权限为ROLE_ADMIN时方可访问,否则抛出权限不足异常
     *
     * @return
     */
    @GetMapping("/admin")
//    @PreAuthorize("hasRole('ROLE_admin')")
    public JsonData index() {
        String username;
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (principal instanceof UserDetails) {
            username = ((UserDetails) principal).getUsername();
        } else {
            username = principal.toString();
        }
        return JsonData.buildSuccess(username);
    }

    /**
     * 当权限为ROLE_USER时方可访问,否则抛出权限不足异常
     *
     * @return
     */
    @GetMapping("/user")
//    @PreAuthorize("hasRole('ROLE_user')")
    public JsonData hello() {
        String username;
        //获取当前用户信息
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (principal instanceof UserDetails) {
            username = ((UserDetails) principal).getUsername();
        } else {
            username = principal.toString();
        }

        return JsonData.buildSuccess(username);
    }

//    @PostMapping(value = "login")
//    public JsonData login(@RequestBody Map<String,String> params)  {
//        UserInfo userInfo = SecurityUtils.login(params.get("username"), params.get("password"), authenticationManager);
//        return JsonData.buildSuccess(userInfo);
//    }

    @RequestMapping("pub")
    public JsonData pub(){
        String username;
        //获取当前用户信息
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (principal instanceof UserDetails) {
            username = ((UserDetails) principal).getUsername();
        } else {
            username = principal.toString();
        }

        return JsonData.buildSuccess(username);
    }

}


6.9、application.properties配置

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123123123

mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#开启Mybatis下划线命名转驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true

server.port=8080
spring.web.resources.static-locations=classpath:/METAINF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/templates/


# REDIS (RedisProperties)
# 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=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000




7、其他

7.1、Spring Security的权限配置不生效问题

在集成Spring Security做接口权限配置时,在给用户配置的权限后,还是一直显示“无权限”或者"权限不足"。
1、不生效的例子:
接口

@RequestMapping("/admin")
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public String printAdmin() {
        return "如果你看见这句话,说明你有ROLE_ADMIN角色";
    }

    @RequestMapping("/user")
    @ResponseBody
    @PreAuthorize("hasRole('USER')")
    public String printUser() {
        return "如果你看见这句话,说明你有ROLE_USER角色";
    }

SecurityConfig

	.and()
      .authorizeRequests()
      .antMatchers("/user").hasAnyRole("USER") 
      .antMatchers("/admin").hasAnyRole("ADMIN")
      .anyRequest().authenticated() //必须授权才能范围

数据库
2、解决办法

经测试,只有用户携带权限的字段为 “ROLE_” + 接口/配置 中的权限字段,才能控制生效,举例:
将上面的用户携带权限改为
数据库

7.2、Bug笔记

没有被spring管理的对象,即自己new的一个对象,其无论如何都不会通过@Autowired注入成功

Spring Boot中由于序列化方式不同因此不能混合使用StringRedisTemplate和RedisTemplate

  • 9
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
好的,我可以为您提供一些指导。 首先,您需要在您的Spring Boot项目中添加Spring SecurityRedis的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 然后,在您的Spring Boot项目中创建一个配置类,该类将配置Spring SecurityRedis的集成。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private RedisConnectionFactory redisConnectionFactory; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasRole("USER") .anyRequest().authenticated() .and() .formLogin() .and() .logout() .logoutSuccessUrl("/"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { RedisUserDetailsService userDetailsService = new RedisUserDetailsService(redisConnectionFactory); auth.userDetailsService(userDetailsService); } @Bean public RedisTokenRepositoryImpl redisTokenRepository() { return new RedisTokenRepositoryImpl(redisConnectionFactory); } @Bean public TokenBasedRememberMeServices rememberMeServices() { TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("remember-me", userDetailsService()); rememberMeServices.setTokenRepository(redisTokenRepository()); return rememberMeServices; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` 上述配置类中的configure(HttpSecurity http)方法指定了不同角色的用户可以访问哪些URL。configure(AuthenticationManagerBuilder auth)方法指定了如何从Redis中获取用户信息。redisTokenRepository()和rememberMeServices()方法指定了如何将Remember-Me令牌存储到Redis中。 最后,在您的Spring Boot项目中创建一个RedisUserDetailsService类,该类将从Redis中获取用户信息。 ```java public class RedisUserDetailsService implements UserDetailsService { private RedisConnectionFactory redisConnectionFactory; public RedisUserDetailsService(RedisConnectionFactory redisConnectionFactory) { this.redisConnectionFactory = redisConnectionFactory; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericToStringSerializer<>(Object.class)); redisTemplate.afterPropertiesSet(); Map<Object, Object> userMap = redisTemplate.opsForHash().entries("user:" + username); if (userMap.isEmpty()) { throw new UsernameNotFoundException("User '" + username + "' not found"); } String password = (String) userMap.get("password"); List<String> authorities = (List<String>) userMap.get("authorities"); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); for (String authority : authorities) { grantedAuthorities.add(new SimpleGrantedAuthority(authority)); } return new User(username, password, grantedAuthorities); } } ``` 上述类中的loadUserByUsername(String username)方法从Redis中获取用户信息。 希望这些信息能对您有所帮助!
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

和烨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值