在众多的开发任务里,权限管理系统开发是常见的也是大部分程序员并着手开发过的系统。在最近的任务,上级要求开发一个通用的基于url的权限控制系统,由于笔者对shiro早有接触,虽然springsecurity的功能强大,与spring易整合但结构复杂组件较多,为了在有限的开发周期内减少学习成本,最后确定技术选型:springboot+shiro+redis+jwt+mybatis+mysql。
设计思路
- shiro配置放行uri,比如登录登出等游客身份也能调用的接口等资源,登陆成功的用户服务器会返回特有的jwt(作为令牌)给前端交由前台保存,可保存到localStorage或sessionStorage,之后该用户的请求的header都需带上jwt
- 因为是基于url的拦截,新建拦截器继承BasicHttpAuthenticationFilter,重写preHadle(),isAccessAllowed().onAccessDenied()方法,主要功能:捕获请求,获取请求头的token进行身份验证,获取请求uri进行权限认证,认证通过才能调用controller接口
- 自定义realm类重写doGetAuthenticationInfo(),doGetAuthorizationInfo()方法,自定义身份认证及权限认证方式,给上面的拦截器所调用
- 生成的jwt会保存到redis交由redis维护,以用户名为key,过期时间30分钟,每次请求经过拦截器时候会刷新过期时间,若reidis获取不到jwt或jwt与请求头的jwt不一致则认证失败返回登陆页重新登陆
上菜
导包(展示部分)
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.0.7.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-support -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-support</artifactId>
<version>2.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.7.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.55</version>
</dependency>
<!-- JWT:Json Web Token 配置 用于请求身份验证 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${
jjwt.version}</version>
</dependency>
<!-- Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${
shiro.version}</version>
</dependency>
<!-- Redis-Jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${
jedis.version}</version>
</dependency>
</dependencies>
配置Jedis
package com.rq.authority.config.redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* Jedis配置,项目启动注入JedisPool
*/
@Configuration
@EnableAutoConfiguration
public class JedisConfig {
/**
* LOGGER
*/
private static final Logger logger = LoggerFactory.getLogger(JedisConfig.class);
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("200")
private int maxActive;
@Value("-1")
private int maxWait;
@Value("8")
private int maxIdle;
@Value("0")
private int minIdle;
@Bean
public JedisPool redisPoolFactory(){
try {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWait);
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMinIdle(minIdle);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
logger.info("初始化Redis连接池JedisPool成功!地址: " + host + ":" + port);
return jedisPool;
} catch (Exception e) {
logger.error("初始化Redis连接池JedisPool异常:" + e.getMessage());
}
return null;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMaxWait() {
return maxWait;
}
public void setMaxWait(int maxWait) {
this.maxWait = maxWait;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle