- 一. 搭建redis基本环境
- ①加入redis相关依赖
- ②写入redis的application.yml配置文件
- 二. shiro认证授权实现
- ①导入shiro相关依赖
- ②配置shiro
- ③redis缓存管理器配置
- ④redis缓存序列化方式配置
- 三. 测试
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
org.springframework.session
spring-session-core
②写入redis的application.yml配置文件
#配置连接redis的参数
spring:
redis:
cluster:
#配置redis集群节点
nodes: 92.168.10.140:8001,192.168.10.140:8002,192.168.10.140:8003,192.168.10.140:8004,192.168.10.140:8005,192.168.10.140:8006
session:
store-type: redis #存放session的存储方式
redis:
flush-mode: on_save #配置session刷新方式
timeout: 30m #设置session超时时间
这样redis的基本环境就搭建完成了。
二. shiro认证授权实现 ①导入shiro相关依赖②配置shiroorg.apache.shiro shiro-spring 1.4.0 org.apache.shiro shiro-web 1.4.0
Shiro 配置类-》ShiroConfig.java
–负责配置shiro的资源配置、安全管理器对象、和管理自定义的realm对象
@Configuration
public class ShiroConfig {
//创建shiroFilter(shiro拦截器对象--负责拦截所有请求)
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//给Filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系统受限资源
//配置系统公共资源
Map map=new HashMap();
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//创建安全管理对象
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置Realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//创建自定义Realm
@Bean
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
//修改凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法为Md5
credentialsMatcher.setHashAlgorithmName("MD5");
//设置散列次数
credentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(credentialsMatcher);
//设置缓存管理器
customerRealm.setCacheManager(new RedisCacheManager());
//开启缓存
customerRealm.setCachingEnabled(true);
//开启认证缓存并指定缓存名称
customerRealm.setAuthenticationCachingEnabled(true);
customerRealm.setAuthenticationCacheName("AuthenticationCache");
//开启授权缓存并指定缓存名称
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setAuthorizationCacheName("AuthorizationCache");
return customerRealm;
}
}
自定义Realm-》 CustomerRealm.java
用来处理用户的认证与授权
public class CustomerRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//授权
//获取身份信息
// String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
// //根据身份信息查取数据库获取 权限 和 角色 信息
// if(primaryPrincipal.equals("xwm")){ //(模拟操作)
// SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
// simpleAuthorizationInfo.addRole("admin");//为用户添加角色
// simpleAuthorizationInfo.addStringPermission("admin:*:*");//admin角色下的所有资源的所有权限
// return simpleAuthorizationInfo; //返回对象
// }
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//认证
String principal = (String)authenticationToken.getPrincipal();//获取用户名
UserServiceImpl userService=(UserServiceImpl) ApplicationContextUtils.getContext("UserService");//获取容器里的Bean
Patient patient = userService.loginUser(principal);//通过姓名获取用户信息
if(patient!=null){//用户名,用户已加密的密码,盐,当前对象
System.out.println("-将patient对象存入session-");//会同时将将patient对象存入session中
return new SimpleAuthenticationInfo(patient,patient.getPatientpwd(),//密码
new CustomerByteSource(patient.getPsalt()),//盐
this.getName());//当前realm
}
return null;
}
}
用来为用户加密产生加密盐值的工具类
可以产生随机的字符串,可以加强加密的安全性。
public class SaltUtils {
public static String getSalt(int n){//快捷键:Ctrl+Shift+U==>可将选中的字母大写转小写
char[] chars = "ABCDEFJHIJKLMNOPQRSTUVWXYZabcdefjhijklmnopqrstuvwxyz0987654321!@#$%^&*():?><|~/}{][`".toCharArray();
StringBuilder str=new StringBuilder();
for (int i = 0; i < n; i++) {
char aChar = chars[new Random().nextInt(chars.length)];
str.append(aChar);
}
return str.toString();
}
public static void main(String[] args) {//盐值生成测试
String str=getSalt(8);
System.out.println(str);
Md5Hash md5Hash=new Md5Hash("123",str,1024);
System.out.println(md5Hash.toHex());
}
}
自定义盐值实现类(由于原本的盐实现是没有序列化的,在redis缓存过程中会出现序列化错误,所以需要自定义一个salt实现,实现序列化接口。)
//自定义salt实现 实现序列化接口
public class CustomerByteSource implements ByteSource, Serializable {
private byte[] bytes;
private String cachedHex;
private String cachedbase64;
public CustomerByteSource() {
}
public CustomerByteSource(byte[] bytes) {
this.bytes = bytes;
}
public CustomerByteSource(char[] chars) {
this.bytes = CodecSupport.toBytes(chars);
}
public CustomerByteSource(String string) {
this.bytes = CodecSupport.toBytes(string);
}
public CustomerByteSource(ByteSource source) {
this.bytes = source.getBytes();
}
public CustomerByteSource(File file) {
this.bytes = (new CustomerByteSource.BytesHelper()).getBytes(file);
}
public CustomerByteSource(InputStream stream) {
this.bytes = (new CustomerByteSource.BytesHelper()).getBytes(stream);
}
public static boolean isCompatible(Object o) {
return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
}
@Override
public byte[] getBytes() {
return this.bytes;
}
@Override
public boolean isEmpty() {
return this.bytes == null || this.bytes.length == 0;
}
@Override
public String toHex() {
if (this.cachedHex == null) {
this.cachedHex = Hex.encodeToString(this.getBytes());
}
return this.cachedHex;
}
@Override
public String tobase64() {
if (this.cachedbase64 == null) {
this.cachedbase64 = base64.encodeToString(this.getBytes());
}
return this.cachedbase64;
}
@Override
public String toString() {
return this.tobase64();
}
@Override
public int hashCode() {
return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof ByteSource) {
ByteSource bs = (ByteSource) o;
return Arrays.equals(this.getBytes(), bs.getBytes());
} else {
return false;
}
}
private static final class BytesHelper extends CodecSupport {
private BytesHelper() {
}
public byte[] getBytes(File file) {
return this.toBytes(file);
}
public byte[] getBytes(InputStream stream) {
return this.toBytes(stream);
}
}
}
③redis缓存管理器配置
通过实现CacheManager接口为shiro配置redis缓存管理器。
public class RedisCacheManager implements CacheManager {
@Override
public Cache getCache(String cacheName) throws CacheException {
System.out.println("缓存名称: "+cacheName);
return new RedisCache(cacheName);
}
}
自定义redis缓存实现RedisCache类,内部都是操作redis数据库的方法。
//存储键值对不应该使用注解方式而应该从容器获取对象,因为这种方式只能存储值为字符串的键值对。 public class RedisCacheimplements Cache { private String cacheName; public RedisCache() { } public RedisCache(String cacheName) { this.cacheName = cacheName; } @Override public V get(K k) throws CacheException { System.out.println("获取缓存:"+ k); return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString()); } @Override public V put(K k, V v) throws CacheException { System.out.println("设置缓存key: "+k+" value:"+v); getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v); return null; } @Override public V remove(K k) throws CacheException { System.out.println("移除缓存key: "+k); return (V) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString()); } @Override public void clear() throws CacheException { getRedisTemplate().delete(this.cacheName); } @Override public int size() { return getRedisTemplate().opsForHash().size(this.cacheName).intValue(); } @Override public Set keys() { return getRedisTemplate().opsForHash().keys(this.cacheName); } @Override public Collection values() { return getRedisTemplate().opsForHash().values(this.cacheName); } private RedisTemplate getRedisTemplate(){//获取redis操作对象 RedisTemplate //通过工具类ApplicationContextUtils指定Bean名来获取容器中指定的Bean对象 RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getContext("redisTemplate"); return redisTemplate; } }
用来从容器里获取Bean对象的工具类ApplicationContextUtils
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext context;
@Override//系统会自动调用该类经工厂传如该函数(由程序自动调用)
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context=applicationContext;
}
//根据bean名字获取工厂里指定的bean对象
public static Object getContext(String name){
Object bean = context.getBean(name);
return bean;
}
}
④redis缓存序列化方式配置
redis配置类 用来配置redis个类型的序列化方式(如果不配置该项,程序会出现反序列化失败的错误)
注:在分布式项目里,如果想要这种方式来实现session共享则必须两端即双方的redis缓存序列化方式都必须一样否则程序会出现序列化的异常错误。从而使程序无法正常运行。
@Bean
public RedisTemplate
三. 测试
在分布式项目中,A模块内通过session.setAttribute("xxx",xxx)将数据存入redis中,再通过ModelAndView view = new ModelAndView(new RedirectView("请求地址"))来重定向到B模块,通过测试B模块的session.getAttribute("xxx")来判断共享是否成功,如果有值则代表session共享成功。



