--
本文记录了 SpringBoot + Shiro整合,包括 Shiro 登录认证(多个 Realm认证),Session、Cache + Redis共享,RememberMe(记住我)等多项功能。
--
一、Pom.xml
4.0.0
com.made
ecifms
0.0.1-SNAPSHOT
war
SpringBootTest
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.9.RELEASE
UTF-8
UTF-8
1.7
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
myOracle
ojdbc6
11.2.0
jar
org.springframework.boot
spring-boot-starter-redis
1.4.5.RELEASE
org.apache.shiro
shiro-core
1.4.0
org.apache.shiro
shiro-spring
1.4.0
org.apache.shiro
shiro-ehcache
1.4.0
org.springframework.boot
spring-boot-devtools
true
org.springframework.boot
spring-boot-starter-tomcat
provided
org.apache.tomcat.embed
tomcat-embed-jasper
provided
javax.servlet.jsp.jstl
jstl-api
1.2
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.1
tk.mybatis
mapper-spring-boot-starter
1.1.4
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.1
org.springframework.boot
spring-boot-maven-plugin
true
public
Public Repository
http://10.1.164.8:15000/nexus/content/groups/public
二、Redis配置
1. application.properties
#配置哨兵
#spring.redis.database=0
#spring.redis.password=made
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
#哨兵监听redis server名称
spring.redis.sentinel.master=mymaster
#哨兵的配置列表
spring.redis.sentinel.nodes=10.1.164.11:26379,10.1.164.12:26379
2. RedisConfig.java
package com.made.common.config;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisSentinelPool;
@Configuration
public class RedisConfig {
//注入集群节点的信息
/*@Value("${spring.redis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.pool.max-active}")
private String maxActive;
@Value("${spring.redis.pool.max-wait}")
private long maxWait;*/
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.sentinel.master}")
private String master;
@Value("${spring.redis.sentinel.nodes}")
private String nodes;
@Bean //
public JedisSentinelPool getJedisSentinelPool(){
//分割集群节点
String[] cNodes = nodes.split(",");
//创建set集合
Set nodes = new HashSet();
//循环集群节点对象
for (String node : cNodes) {
//String[] hp = node.split(":");
nodes.add(node);
System.out.println(node);
}
/*JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxWaitMillis(maxWait);*/
//JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, nodes, poolConfig, password);
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(master, nodes, password);
return jedisSentinelPool;
}
}
3. RedisService.java
package com.made.common.redis;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import com.made.common.CommonDefinitionUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
@Component
public class RedisService {
private static Logger logger = LoggerFactory.getLogger(RedisService.class);
@Autowired
private JedisSentinelPool jedisPool;
/**
* get value from redis
* @param key
* @return
*/
public byte[] get(byte[] key){
byte[] value = null;
Jedis jedis = jedisPool.getResource();
try{
value = jedis.get(key);
} catch(Exception e) {
logger.error("redis key:{} get value occur exception", new String(key));
} finally{
jedis.close();
}
return value;
}
/**
* set
* @param key
* @param value
* @return
*/
public byte[] set(byte[] key,byte[] value){
Jedis jedis = jedisPool.getResource();
try{
jedis.set(key,value);
jedis.expire(key, (int) CommonDefinitionUtil.SHIRO_SESSION_TIME);
} catch(Exception e) {
logger.error("redis key:{} set value:{} occur exception", new String(key), new String(value));
} finally{
jedis.close();
}
return value;
}
/**
* set
* @param key
* @param value
* @param expire
* @return
*/
public byte[] set(byte[] key,byte[] value,int expire){
Jedis jedis = jedisPool.getResource();
try{
jedis.set(key,value);
if(expire != 0){
jedis.expire(key, expire);
}
} catch(Exception e) {
logger.error("redis key:{} set value:{} in expire:{} occur exception",
new String(key), new String(value), expire);
} finally{
jedis.close();
}
return value;
}
/**
* del
* @param key
*/
public void del(byte[] key){
Jedis jedis = jedisPool.getResource();
try{
jedis.del(key);
} catch(Exception e) {
logger.error("redis key:{} del value occur exception", new String(key));
} finally{
jedis.close();
}
}
/**
* flush
*/
public void flushDB(){
Jedis jedis = jedisPool.getResource();
try{
jedis.flushDB();
} catch(Exception e) {
logger.error("redis flushDB occur exception");
} finally{
jedis.close();
}
}
/**
* size
*/
public Long dbSize(){
Long dbSize = 0L;
Jedis jedis = jedisPool.getResource();
try{
dbSize = jedis.dbSize();
} catch(Exception e) {
logger.error("redis get dbSize occur exception");
} finally{
jedis.close();
}
return dbSize;
}
/**
* keys
* @param regex
* @return
*/
public Set keys(String pattern){
Set keys = null;
Jedis jedis = jedisPool.getResource();
try{
keys = jedis.keys(pattern.getBytes());
} catch(Exception e) {
logger.error("redis get keys in pattern:{} occur exception", pattern);
} finally{
jedis.close();
}
return keys;
}
/**
* 判断key是否存在
*/
public Boolean exists(String key) {
Jedis jedis = jedisPool.getResource();
Boolean result = jedis.exists(key);
jedis.close();
return result;
}
/**
* 设置失效时间
*/
public Long expire(String key, int seconds) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.expire(key, seconds);
jedis.close();
return result;
}
/**
* key-value 存数据到redis
*/
public String set(String key, String value) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(key, value);
jedis.close();
return result;
}
/**
* 根据key,获取值
*/
public String get(String key) {
Jedis jedis = jedisPool.getResource();
String result = jedis.get(key);
jedis.close();
return result;
}
/**
* 根据key,删除数据
*/
/*public Long del(String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.del(key);
jedis.close();
return result;
}*/
}
三、Realm认证(多个Realm认证)
1. UsernamePasswordTokenCustom.java
首先需要继承 UsernamePasswordToken类,新增一个属性,用来标识登录类型。
package com.made.common.shiro.token;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* 自定义 UsernamePasswordToken
* @author majunde
*
*/
public class UsernamePasswordTokenCustom extends UsernamePasswordToken{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 登录类型: 普通登录,SSO登录
*/
private String loginType;
/**
* 构造函数
* @param username
* @param password
* @param loginType 登录类型
*/
public UsernamePasswordTokenCustom(String username, String password, String loginType) {
super(username, password);
this.loginType = loginType;
}
public UsernamePasswordTokenCustom(String username, String password, String loginType, boolean rememberMe) {
super(username, password, rememberMe);
// TODO Auto-generated constructor stub
this.loginType = loginType;
}
public UsernamePasswordTokenCustom(String username, String password, boolean rememberMe) {
super(username, password, rememberMe);
// TODO Auto-generated constructor stub
}
public UsernamePasswordTokenCustom(String username, String password) {
super(username, password);
// TODO Auto-generated constructor stub
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
}
2. ModularRealmAuthenticatorCustom.java
其次,还需要继承与ModularRealmAuthenticator,重写其中的
doAuthenticate(AuthenticationToken authenticationToken)方法。
package com.made.common.shiro.realm;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import com.made.common.shiro.token.UsernamePasswordTokenCustom;
/**
* 自定义 ModularRealmAuthenticator
* 由该类 决定 分配调用 那个 realm, 默认全部调用
* @author majunde
*
*/
public class ModularRealmAuthenticatorCustom extends ModularRealmAuthenticator{
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
// TODO Auto-generated method stub
// 判断 getRealms(); 是否为空
assertRealmsConfigured();
Collection realms = getRealms();
// 获取 token 得到 loginType
UsernamePasswordTokenCustom token = (UsernamePasswordTokenCustom) authenticationToken;
String loginType = token.getLoginType();
Collection typeRealms = new ArrayList<>();
for (Realm realm : realms) {
// 如果 loginTyppe为空,代表默认
if (loginType == null) {
if (realm.getName().contains("Default")) {
typeRealms.add(realm);
break;
}
continue;
}
if (realm.getName().contains(loginType)) {
typeRealms.add(realm);
break;
}
}
if (typeRealms.size() == 1) {
return doSingleRealmAuthentication(typeRealms.iterator().next(), authenticationToken);
}
// 改变 realms --> typeRealms
return doMultiRealmAuthentication(typeRealms, authenticationToken);
}
}
3.LoginTypeEnum.java
另外,为了标识 登录类型,使用了枚举来记录用户的登录类型。
package com.made.common.shiro.token;
/**
* 登录类型
* @author majunde
*
*/
public enum LoginTypeEnum {
/**
* 默认登录
*/
Default("Default"),
/**
* SSO 登录
*/
Sso("Sso");
private final String value;
private LoginTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}