(1)安装
从
上下载 Redis 3.2.100(这是解压免安装版的最高版本)。(2)启动
redis-server.exe redis.windows.conf
redis-server.exe #简写。有些版本的 Windows 系统无法简写,原因不明。
(3)客户端连接到Redis Server
进入 redis 的解压目录,打开一个命令行终端,执行如下命令:
redis-cli.exe
redis-cli.exe -h # 完整格式:<指定ip> -p <指定端口> -a <指定密码>
(4)关闭
进入 redis 的解压目录,打开一个命令行终端,执行如下命令:
./redis-cli.exe -h 127.0.0.1 -p 6379 shutdown
./redis-cli.exe shutdown # 简写
(补充)借助 WinSW 启停 Redis
第 1 步:下载 Windows Service Wrapper 工具
将下载好的 WinSW 放到 REDIS_HOME 目录下,并重命名。名字任意,例如:systemctl.exe 。
第 2 步:为 WinSW 创建配置文件
在 REDIS_HOME 下为 WinSW 创建配置文件( 配置文件与 WinSW 程序平级 )。配置文件为 .xml 文件,且名字必须与 WinSW 程序相同。例如:systemctl.xml ,与上面的 systemctl.exe 相呼应。
systemctl.xml 配置文件内容如下:
<service>
<id>redis-serverid>
<name>Redis Servername>
<description>Redis Serverdescription>
<executable>%BASE%\redis-server.exeexecutable>
<startargument>redis.windows.confstartargument>
<stopexecutable>%BASE%\redis-cli.exestopexecutable>
<stopargument>shutdownstopargument>
<logpath>%BASE%\logslogpath>
<logmode>rolllogmode>
service>
在上述的配置文件中,我们「告知」了 WinSW 以什么命令启停 Redis Server 。未来,我们不再亲自启停 Redis Server ,而是通过 WinSW 间接启停 Redis Server 。
第 3 步:安装 Redis Server 服务
在 REDIS_HOME 目录下打开 cmd 命令行执行如下命令:
# 安装服务。开机启动,当前未启动,重启后生效。
systemctl install
# 如果对「开机启动」有异议,可通过 Windows 的 sc 命令调整
sc config redis-server start= demand # 手动启动
# sc config <服务名称> start= auto # 开机启动,当前未启动,重启后生效
# sc config <服务名称> start= disabled # 禁用
安装成功后,你可以在 Windows 系统的服务中看到 Redis Server 。
数据模型
Redis 数据模型不仅仅与关系数据库管理系统(RDBMS)不同,也不同于任何简单的 NoSQL 键-值数据存储系统。
Redis 数据类型类似于编程语言的基础数据类型,因此开发人员感觉很自然,每个数据类型都支持适『用于其类型的操作』,以最大限度发挥每种数据类型的特性。
受支持的数据类型包括:
- string( 字符串 )
- hash( 哈希 )
- list( 列表 )
- set( 集合 )
- zset( sorted set:有序集合 )
关键优势
Redis 的优势包括它的速度、对富数据类型的支持、操作的原子性,以及通用性:
性能极高,它每秒可执行约 10k 个 SET 以及约 100k 个 GET 操作;
丰富的数据类型,Redis 对大多数开发人员已知的多数数据类型提供了原生支持,这使得各种问题得以轻松解决;
原子性,因为所有 Redis 操作都是原子性的,所以多个客户端会并发地访问一个 Redis 服务器器,获取相同的更新值;
丰富的特性,Redis 是一个多效用工具,有非常多的应用场景,包括缓存、消息队列( Redis 原生支持发布/订阅 )、短期应用程序数据据( 比如 Web 会话 )等。
Redis 的『快』的原因
1.是对内存的较为简单的数据进行读写操作。
2.Redis 是 C 语言实现的,它的底层实现原理是基于 IO 多路复用技术。这个实现方案本身就是先进/高级/高效的。
3.Redis 的工作是基于单线程的,从而节省了多线程形式下线程切换的开销。
一次只运行一条命令,每条命令天生就是一个独立的事务。
拒绝使用长/慢命令,Redis 对外提供的每条命令都很高效快速。
其实并不是单线程。有些操作使用独立线程,其它的线程是去干其它的事情,和执行命令的工作线程无关
最后的事实也证明了 Redis 的作者的最初分析思路的正确性:Redis 的单机性能的瓶颈是网络速度和网卡性能,而非 CPU 。
Redis 的通用命令简介
一个 Redis 实例可以包括多个数据库。不过,一个 redis 实例最多可提供 16 个数据库,而且固定了以下标从 0 到 15 作为数据库名。客户端默认连接第 0 号数据库。
可以通过 select 命令来当前数据库,如果选择一个不存在数据库则会报错。
select N
命令 | 说明 |
---|---|
ping | PING 命令来测试客户端与 Redis 的连接是否正常。 连接正常时会收到回复 PONG |
set / get | 使用 set 和 get 可以向 redis 设置数据、获取数据。 |
del | 删除指定 key 的内容。 |
Keys * | 查看当前库中所有的 key 值 |
Redis 单机多实例
如果需要启动两个 Redis 实例,你可以这么干:
1.在 Redis 解压目录下创建两个文件夹,例如:6379 和 16379 。以各自所占端口号命名。
2.将 redis.windows.conf 文件复制进这两个文件夹,并将 16379 中的配置文件中的 port 6379 改为 port 16379 。
3.在这两个文件夹中,分别启动两个命令行,执行 ../redis-server.exe redis.windows.conf 命令。
4.未来,在使用 redis-cli.exe 连接 Redis 服务端的时候,就需要明确指定连接端口号。
持久化
Redis 的高性能是由于其将所有数据都存储在了内存中,为了使 Redis 在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
Redis支持两种方式的持久化,一种是 RDB 方式,一种是 AOF 方式。
RDB 方式
RDB 方式是 Redis 的默认持久化方式。
它是通过快照( snapshotting )完成的,当符合一定条件时 Redis 会自动将内存中的数据进行快照并持久化到硬盘。简单来说,就是直接将内存中的数据直接保存到硬盘上。
在 redis.windows.conf 配置文件中默认有此下配置:
save 900 1
save 300 10
save 60 10000
save 开头的一行就是持久化配置,可以配置多个条件( 每行配置一个条件 ),每个条件之间是『或』的关系,save 900 1
表示 900 秒钟( 15 分钟 )内至少 1 个键被更改则进行快照,save 300 10
表示 300 秒( 5 分钟 )内至少 10 个键被更改则进行快照。
Redis 启动后会读取 RDB 快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为 1GB 的快照文件载入到内存中需要花费 20~30 秒钟。
但是 RDB 方式实现持久化有个问题啊:一旦 Redis 异常( 突然 )退出,就会丢失最后一次快照以后更改的所有数据。因此在使用 RDB 方式时,需要根据实际情况,调整配置中的参数,以便将数据的遗失控制在可接受范围内。
AOF 方式
默认情况下 Redis 没有开启 AOF( append only file )方式的持久化,可以通过配置文件中的 appendonly 参数开启:
appendonly yes
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 AOF 文件。AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof,可以通过 appendfilename 参数修改:appendfilename appendonly.aof
# | 命令 | 说明 |
---|---|---|
1 | keys * | 查看所有的 key |
2 | dbsize | 统计键值对的数量 |
3 | exists key | 判断指定键值对是否存在,存在则返回 1,不存在返回 0 |
4 | del key | 删除指定键值对 |
5 | expire key seconds ttl key persist key | 设置过期自动删除 查询 key 的剩余过期时间。 (返回 -1 表示没有过期设置;返回 -2 表示过期删除。) 去掉 key 的过期设置 |
6 | type | 查看键值对数据模型类型 |
增、改:set
增、改:set NX (key 不存在,才设置)
增、改:set XX (key 存在,才设置)
删:del
查:get
增、改:hset (例:hset user name xiaoming)
批量增:Hmset [field2 value2] ...
删:Hdel [ field2 ... ]
查:hget (例:hget user name)
查询某个key是否存在:Hexists
增:Lpush ... (从key的左边插入数据,如果不存在则创建)
增:Rpush ... (从key的右边插入数据,如果不存在则创建)
增:Linsert [before | after] (在key的value之前或者之后插入元素)
删:Lpop (删除key下面的最左边的元素)
删:Rpop (删除key下面的最右边的元素)
删:Lrem
Lrem 根据参数 count 的值,移除某个列表中与参数 value 相等的元素。
count 的值可以是以下几种:
count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 count 。
count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 count 的绝对值。
count = 0 : 移除表中所有与 VALUE 相等的值。
查批量:Lrange 开始索引 结束索引
查单个:Lindex
查长度:Llen
相当于对value去重的哈希
增:Sadd ...
删:Spop key (随机移除一个元素)
删:Srem ... (移除指定个元素)
改:SMOVE
和『哈希』有点类似,有序集合中的键值对的值中,也是有两个部分:score 和 value 。
score 的值决定了与之对应的 value 的顺序
zadd
zrem
zscore
zincrby
zcard
zrange
zrangebyscore
zcount
zremrangebyrank
zremrangebyscore
(1)Zadd 命令
Zadd 命令用于将一个或多个成员元素及其分数值加入到某个有序集当中。
如果某个成员已经是有序集的成员,那么更新这个成员的分数值。有序集合内部会重新调整成员元素的位置,来保证这个集合的有序性。
分数值可以是整数值或双精度浮点数。通常使用整数值。
如果有序集合不存在,则创建一个空的有序集并执行 Zadd 操作。
当 key 所对应的并非有序集类型时,返回一个错误。
语法:
Zadd [scoren value ... ]
Zadd 命令将被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。
> ZADD set1 1 "hello"
(integer) 1
> ZADD set1 1 "foo"
(integer) 1
> ZADD set1 2 "world" 3 "bar"
(integer) 2
> ZRANGE set1 0 -1 WITHSCORES
1) "hello"
2) "1"
3) "foo"
4) "1"
5) "world"
6) "2"
7) "bar"
8) "3"
(2)Zcard 命令
Zcard 命令用于计算某个集合中元素的数量。
语法:
Zcard
当集合存在时,Zcard 返回有序集的基数;当集合不存在时,返回 0 。
> ZADD myset 1 "hello"
(integer) 1
> ZADD myset 1 "foo"
(integer) 1
> ZADD myset 2 "world" 3 "bar"
(integer) 2
> ZCARD myzset
(integer) 4
(3)Zcount 命令
Zcount 命令用于计算某有序集合中指定分数区间的成员数量。
语法:
Zcount
Zcount 返回分数值在 min 和 max 之间的成员的数量。
> ZADD myzset 1 "hello"
(integer) 1
> ZADD myzset 1 "foo"
(integer) 1
> ZADD myzset 2 "world" 3 "bar"
(integer) 2
> ZCOUNT myzset 1 3
(integer) 4
(4)Zincrby 命令
Zincrby 命令对某有序集合中指定成员的分数加上增量 increment
可以通过传递一个负数值 increment ,让分数减去相应的值,比如 ZINCRBY key -5 member ,就是让 member 的 score 值减去 5 。
当有序集合不存在,或有序集合中不存在指定分数时, Zincrby 等同于 Zadd 。
当 key 对应的不是有序集时,返回一个错误。
分数值可以是整数值或双精度浮点数。
语法:
Zincrby
Zincrby 命令返回参数 member
的新的分数值(以字符串形式表示)。
> ZADD myzset 1 "hello"
(integer) 1
> ZADD myzset 1 "foo"
(integer) 1
> ZINCRBY myzset 2 "hello"
(integer) 3
> ZRANGE myzset 0 -1 WITHSCORES
1) "foo"
2) "2"
3) "hello"
4) "3"
(5)Zrem 命令
Zrem 命令用于移除某个有序集中的一个或多个成员,不存在的成员将被忽略。
如果 key 对应的并非是有序集类型,则返回一个错误。
语法:
Zrem
# 基本示例
> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
5) "google.com"
6) "10"
# 移除单个元素
> ZREM page_rank google.com
(integer) 1
> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
# 移除多个元素
> ZREM page_rank baidu.com bing.com
(integer) 2
> ZRANGE page_rank 0 -1 WITHSCORES
(empty list or set)
# 移除不存在元素
> ZREM page_rank non-exists-element
(integer) 0
Zrange 返回某有序集中,指定区间内的成员。
如果需要逆序显示,请使用 Zrevrange 命令。
语法:
Zrange [ WITHSCORES ]
Zrange 命令将指定区间内,带有分数值(可选)的有序集成员的列表。
# 显示整个有序集成员
> ZRANGE salary 0 -1 WITHSCORES
1) "jack"
2) "3500"
3) "tom"
4) "5000"
5) "boss"
6) "10086"
# 显示有序集下标区间 1 至 2 的成员
> ZRANGE salary 1 2 WITHSCORES
1) "tom"
2) "5000"
3) "boss"
4) "10086"
# 测试 end 下标超出最大下标时的情况
> ZRANGE salary 0 200000 WITHSCORES
1) "jack"
2) "3500"
3) "tom"
4) "5000"
5) "boss"
6) "10086"
# 测试当给定区间不存在于有序集时的情况
> ZRANGE salary 200000 3000000 WITHSCORES
(empty list or set)
(7)Zrank 命令
Zrank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。
语法:
ZRANK
如果成员是有序集的成员,ZRANK 返回 member 的排名;如果成员不是有序集的成员,返回 nil 。
# 显示所有成员及其 score 值
> ZRANGE salary 0 -1 WITHSCORES
1) "peter"
2) "3500"
3) "tom"
4) "4000"
5) "jack"
6) "5000"
# 显示 tom 的薪水排名,第二
> ZRANK salary tom
(integer) 1
(8)Zrevrank 命令
Zrevrank 命令返回有序集中成员的排名。其中有序集成员按分数值降序排序。
使用 Zrank 命令可以获得成员按分数值升序排序。
语法:
Zrevrank
如果成员是有序集的成员,Zrevrank 返回成员的排名;如果成员不是有序集的成员,返回 nil 。
> ZRANGE salary 0 -1 WITHSCORES # 测试数据
1) "jack"
2) "2000"
3) "peter"
4) "3500"
5) "tom"
6) "5000"
> ZREVRANK salary peter # peter 的工资排第二
(integer) 1
> ZREVRANK salary tom # tom 的工资最高
(integer) 0
(9)Zscore 命令
Zscore 命令返回有序集中,成员的分数值。
如果成员元素不是有序集的成员,或有序集合不存在,返回 nil 。
语法:
Zscore
Zscore 返回成员的分数值(以字符串形式表示)。
> ZRANGE salary 0 -1 WITHSCORES # 测试数据
1) "tom"
2) "2000"
3) "peter"
4) "3500"
5) "jack"
6) "5000"
> ZSCORE salary peter # 注意返回值是字符串
"3500"
步骤1:添加依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
步骤2:登录成功后,把用户权限集存储到redis中
MyAuthenticationSuccessHandler.java
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Resource
private RedisTemplate redisTemplate;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
try {
//获取当前登录认证成功的用户名
String username = request.getParameter("username");
String strToken = JwtTokenUtil.createSign(username);
//将authorities存入redis数据库
Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
StringBuilder sb = new StringBuilder("");
for (GrantedAuthority authority : authorities) {
sb = sb.append(authority).append(",");
}
String auth = sb.substring(0, sb.length() - 1);
ValueOperations stringObjectValueOperations = redisTemplate.opsForValue();
stringObjectValueOperations.set(username, auth);
System.out.println(stringObjectValueOperations.get("username"));
///通过响应的json返回客户端
ResponseResult success = new ResponseResult<>(strToken,"ok",200);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(new ObjectMapper().writeValueAsString(success));
} catch (Exception e) {
e.printStackTrace();
}
}
}
步骤3:发送下一次非认证请求时,从redis中读取用户权限集
JwtTokenAuthenticationFilter.java
/**
* 将用户请求中携带的 JWT 转化为 Authentication Token
* 存在 Spring Security 上下文( Context )
*/
@Component
public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {
//OncePerRequestFilter-每次请求只执行该过滤器一次
@Autowired
private UserMapper userMapper;
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Resource
private RedisTemplate redisTemplate;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String uri = request.getRequestURI();
//判断是否属于认证请求,如果属于,放行
if(uri.endsWith("/login")){
filterChain.doFilter(request, response);
return;
}
//获取请求头中的token串,判断token是否为空
String strToken = request.getHeader("strToken");
if(StringUtil.isEmpty(strToken)){
//如果为空,抛自定义异常-token为空
myAuthenticationFailureHandler.onAuthenticationFailure(request, response,new TokenIsNullException("token为空!"));
}
//token不是空,且不是认证请求
if(JwtTokenUtil.checkSign(strToken)){
String username = JwtTokenUtil.getUserId(strToken);
//查询用户的权限集
// List permList = userMapper.getPerCodesByPerm(username);
//从redis缓存中读取用户权限集
ValueOperations stringObjectValueOperations = redisTemplate.opsForValue();
String authorities = stringObjectValueOperations.get(username).replace("\"", "");
String[] strings = authorities.split(",");
//封装用户的权限集
List auth = new ArrayList<>();
for (String s : strings) {
auth.add(new SimpleGrantedAuthority(s));
}
// //封装用户的权限集
// List authorities = new ArrayList<>();
// strings.forEach(perm->{
// authorities.add(new SimpleGrantedAuthority(perm));
// });
//封装数据库存储的用户信息
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
= new UsernamePasswordAuthenticationToken(username, null,auth);
//存入securityContext
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
filterChain.doFilter(request, response);
}else{
myAuthenticationFailureHandler.onAuthenticationFailure(request, response,new TokenIsInvalidException("token无效!"));
}
}
}
步骤4:注销成功后,清除存储在redis中的用户权限集
MyLogoutSuccessHandler.java
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Resource
private RedisTemplate redisTemplate;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String strToken = request.getHeader("strToken");
if(!StringUtil.isEmpty(strToken)){
SecurityContextHolder.clearContext();
//清除用户存在redis缓存中的权限集
String username = JwtTokenUtil.getUserId(strToken);
redisTemplate.delete(username);
ResponseResult result = new ResponseResult<>("","注销成功",200);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
}else{
ResponseResult result = ResponseResult.error(ResultCode.TOKEN_IS_NULL);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
}
}
}
进行重复的数据库查询时,如果每次都要连接MySQL数据库查询,会造成资源的浪费,因此可以把第一次查询的结果存储到redis数据库中,下次执行相同的查询操作时,就从redis中读取数据。
Spring Data Redis 从 1.7 开始提供 Redis Repositories ,可以无缝的转换并存储 domain objects,使用的数据类型为哈希( hash )。
步骤1:启用 Repository 功能
@Configuration
@EnableRedisRepositories(basePackages = "com.yebuxiu.dao.redis")
public class RedisConfig {
}
步骤2:注解需要缓存的实体
添加关键的两个注解 @RedisHash
和 @Id
;
@Data
@NoArgsConstructor
@AllArgsConstructor
@RedisHash("department")
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String name;
private String location;
}
步骤3:创建一个 Repository 接口
自定的 Repository 接口必须继承 CrudRepository ,才能「天生」具有存取数据的能力。
@Repository
public interface DepartmentRedisDao extends CrudRepository {
}
步骤4:测试查询
@Slf4j
@Repository
@RequiredArgsConstructor
public class DepartmentRepository {
private final DepartmentRedisDao departmentRedisDao;
private final DepartmentMysqlDao departmentMysqlDao;
public Department getById(Long id) {
Optional departmentBox = departmentRedisDao.findById(id);
Department department = null;
try {
department = departmentBox.get();
log.info("从Redis中读取的数据");
} catch (Exception e) {
e.printStackTrace();
log.info("Redis中没有,需要去查数据库");
department = departmentMysqlDao.selectById(id);
}
return department;
}
public void update(Long id, String newName,String newLocation) {
Department department = new Department(id, newName, newLocation);
//修改数据库
departmentMysqlDao.updateById(department);
//州除redis
departmentRedisDao.deleteById(id);
}
}
步骤1:前台新增页面添加一个隐藏的输入框
定义一个onload方法,每次加载前台页面时都通过axios请求,获取一个唯一的uuid,用于标识这次新增操作
Title
步骤2:后台写获取uuid的方法
@RestController
@RequestMapping("/department")
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
@Resource
private RedisTemplate redisTemplate;
@RequestMapping("getUuid")
//获取uuid,传到前台并存入redis缓存
public ResponseResult getUuid(){
String uuid = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(uuid, uuid);
ResponseResult responseResult = new ResponseResult<>(uuid,"ok",200);
return responseResult;
}
}
步骤3:后台从redis中删除前台表单提交uuid
那么就存在两种情况:
第一次提交:uuid存在,执行删除操作,删除成功,执行添加操作
重复提交:uuid已经被删除,执行删除操作,删除失败,不执行添加操作
@RestController
@RequestMapping("/department")
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
@Resource
private RedisTemplate redisTemplate;
@RequestMapping("add")
public String add(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("uuid") String uuid
){
Boolean delete = redisTemplate.delete(uuid);
if(delete){
System.out.println("执行添加操作");
return "success";
}else {
System.out.println("重复提交表单,不执行添加操作");
return "failure";
}
}
}
解决方案: 乐观锁方案
步骤1: 在数据库表中添加一个int类型字段version,并赋予默认值1或者0
步骤2:前端获取到version值,在打开修改界面时将version进行传值
步骤3:后端方法在更新前判断version值是否已经改变,如果改变则更新失败返回0
@Transactional
public Integer updateEmployeeById(Employee employee) {
QueryWrapper wrapper = new QueryWrapper<>();
//判断version值是否相等
wrapper.eq("version", employee.getVersion());
wrapper.eq("id", employee.getId());
Integer update = employeeMapper.update(employee, wrapper);
//如果更新失败则返回0
if (update == 0){
return 0;
}
//更新成功后再将version值加一
//如果自己写动态sql则不需要后面
employee.setVersion(employee.getVersion()+1);
employeeMapper.updateById(employee);
return update;
}
步骤4:判断更新的结果后发送对应ResponseResult
@RequestMapping("/update")
@PreAuthorize("hasAnyAuthority('all')")
public ResponseResult updateUser(@RequestBody Employee employee){
Integer integer = employeeService.updateEmployeeById(employee);
ResponseResult result = new ResponseResult<>();
if (integer == 0){
result = new ResponseResult<>(404, "failure");
}else {
result = ResponseResult.ok();
}
return result;
}
步骤5:前端通过获取对应的结果进行更新判断
updateUser() {
this.$axios.post('/api/employee/update', this.objUser)
.then(res => {
console.log(res)
if (res.data.status === 200) {
this.$message({
showClose: true,
message: '修改成功',
type: 'success',
duration: 2000, //弹窗停留时间
offset: 25, //设置弹窗与顶部偏移距离
});
this.dialogFormVisible = false;
this.$emit('func')
} else if (res.data.status === 404) {
this.$message({
showClose: true,
message: '该用户信息已经改变',
type: 'error',
duration: 2000, //弹窗停留时间
offset: 25, //设置弹窗与顶部偏移距离
});
// this.dialogFormVisible = true;
}
})
.catch(err => {
this.$message({
showClose: true,
message: '服务器跑路了,请联系管理员',
type: 'error',
duration: 2000, //弹窗停留时间
offset: 25, //设置弹窗与顶部偏移距离
});
// console.error(err);
this.$emit('func')
})
},
步骤1:在webSecurityconfig配置类的config方法中增加可访问路径
方法一:
@Override
protected void configure(HttpSecurity http) throws Exception {
//http.httpBasic().and().authorizeRequests().anyRequest().authenticated(); //关闭httpBasic认证
//需要放行的url在这里配置,必须要放行/login和/login.html,不然会报错
http.authorizeRequests()
//增加可访问的匿名对象
.antMatchers("/login", "/login.html","/employee/list","/department/list").permitAll()
//此方法会让匿名对象直接访问,但是有权限用户无法访问
//.antMatchers("/employee/list","/department/list").anonymous()
.anyRequest().authenticated().and().
// 设置登陆页、登录表单form中action的地址,也就是处理认证请求的路径
formLogin().loginPage("/login.html").loginProcessingUrl("/login")
//登录表单form中密码输入框input的name名,不修改的话默认是password
.usernameParameter("username").passwordParameter("password")
//登录认证成功后默认转跳的路径
.defaultSuccessUrl("/home")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler)
.and().exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint)
.accessDeniedHandler(myAccessDeniedHandler)
.and().logout().logoutSuccessHandler(myLogoutSuccessHandler);
//出异常后执行的路径,此方法为重定向,源码将错误信息存储在session中,如果使用
//failureForwardUrl则为请求,源码将错误信息存储在请求作用域中
// .failureUrl("/error1").permitAll();
//将自定义的JwtTokenAuthenticationFilter插入到过滤器链中的指定的过滤器前面
http.addFilterBefore(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
//关闭CSRF跨域
http.csrf().disable();
//关闭session最严格的策略 -jwt认证的情况下,不需要security会话参与
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
方法二:
在controller类中的方法上添加注解@PreAuthorize("permitAll()")
@RequestMapping("/list")
@PreAuthorize("permitAll()")
public ResponseResult> getList(){
List list = employeeService.list();
ResponseResult> result = new ResponseResult<>(list,"ok",200);
return result;
}
步骤2:在新增的jwt过滤器中判断token为空时进行放行
if (StringUtils.isEmpty(strToken)){
//Token验证为空
//myAuthenticationFailureHandler.onAuthenticationFailure(request, response,new TokenIsNullException("Token为空"));
filterChain.doFilter(request, response);
return;
}
步骤3:在登录页面添加游客访问按钮
游客访问
anonymous()
{
this.$router.push("/home")
}