您的位置:首页 > 数据库 > Redis

springboot+shiro+redis(单机redis版)整合教程

2018-09-07 13:45 1031 查看

 

相关教程:

 1. springboot+shiro整合教程

 2. springboot+shiro+redis(集群redis版)整合教程

 3. springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)

 

本教程整合环境: java8 maven redis(单机)

开发工具: idea

版本: springboot 1.5.15.RELEASE

 

注:

1.本教程数据操作是模拟数据库操作,并没有真正进行持久化,自行修改即可。

2.角色权限验证未实现,只实现基本的登录验证,自行扩展即可。

 

项目结构:

 

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>webapp</groupId>
<artifactId>springboot-shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>springboot-shiro</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.15.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

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

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

application.yml:

server:
port: 1001
spring:
redis:
cache:
host: localhost
port: 6379
password:
timeout: 5000

User.java:

package webapp.model;

import lombok.Data;

/**
* Created by Administrator on 2018/9/5.
*/
@Data
public class User {
private Long id;
private String userName;
private String password;
}

UserService.java:

package webapp.service;

import webapp.model.User;

/**
* Created by Administrator on 2018/9/5.
*/
public interface UserService {
User findOneByUserName(String userName);
}

UserServiceImpl.java:

package webapp.service.impl;

import org.springframework.stereotype.Service;
import webapp.model.User;
import webapp.service.UserService;

/**
* Created by Administrator on 2018/9/5.
*/
@Service
public class UserServiceImpl implements UserService {

@Override
public User findOneByUserName(String userName) {
User user = new User();
user.setId(1L);
user.setUserName("007少侠");
user.setPassword("123456");
return user;
}
}

UserController.java:

package webapp.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import webapp.service.UserService;

import javax.annotation.Resource;
import java.io.Serializable;

/**
* Created by Administrator on 2018/9/5.
*/
@RestController
@RequestMapping("/core/user")
public class UserController {
@Autowired
private UserService userService;

/**
* 登录
* @param
* @return
*/
@GetMapping("/login")
public String login(String userName, String password) {
System.out.println("登录" + userName);

Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
subject.login(token);

Session session = subject.getSession();
Serializable sessionId = session.getId();
System.out.println("登录成功 -> " + sessionId);

return userName + "[" + sessionId + "]";
}

@GetMapping("/logout")
public String logout() {
SecurityUtils.getSubject().logout();
return "退出登录成功";
}

/**
* 获取当前登录用户
* @return
*/
@GetMapping("/findUser")
public String findUser() {
Subject subject = SecurityUtils.getSubject();
PrincipalCollection collection = subject.getPrincipals();
if (null != collection && !collection.isEmpty()) {
String userName = (String) collection.iterator().next();
System.out.println("获取当前登录用户" + userName);
return userService.findOneByUserName(userName).toString();
}
return "{\n" +
"    \"codeEnum\": \"OVERTIME\",\n" +
"    \"code\": 0,\n" +
"    \"data\": null,\n" +
"    \"msg\": \"未登陆/登陆超时\",\n" +
"    \"success\": false\n" +
"}";
}
}

 

单机版redis相关配置(JedisConfig.java、RedisCache.java)(此2个类也可以单独整合于springboot):

JedisConfig.java(其中只引入了JedisPool的4个基本属性,其他属性使用了默认值,可以自行配置其他属性):

package webapp.conf;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Configuration
@ConditionalOnClass({ Jedis.class })
public class JedisConfig {
@Value("${spring.redis.cache.host}")
private String host;
@Value("${spring.redis.cache.port}")
private int port;
@Value("${spring.redis.cache.password}")
private String password;
@Value("${spring.redis.cache.timeout}")
private int timeout;

@Bean
public Jedis getJedis() {
JedisPool jedisPool;
if (password == null || "".equals(password)) {
jedisPool = new JedisPool(new GenericObjectPoolConfig(), host, port, timeout);
} else {
jedisPool = new JedisPool(new GenericObjectPoolConfig(), host, port, timeout, password);
}
return jedisPool.getResource();
}

}

RedisCache.java:

package webapp.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

@Component
public class RedisCache {
@Autowired
private Jedis jedis;

/**
* 添加缓存数据
* @param key
* @param obj
* @param <T>
* @return
* @throws Exception
*/
public <T> String putCache(String key, T obj) throws Exception {
final byte[] bkey = key.getBytes();
final byte[] bvalue = serializeObj(obj);
return jedis.set(bkey,bvalue);
}

/**
* 添加缓存数据,设定缓存失效时间
* @param key
* @param obj
* @param expireTime 秒
* @param <T>
* @throws Exception
*/
public <T> String putCacheWithExpireTime(String key, T obj, final int expireTime) throws Exception {
final byte[] bkey = key.getBytes();
final byte[] bvalue = serializeObj(obj);
String result = jedis.setex(bkey, expireTime,bvalue);
return result;
}

/**
* 根据key取缓存数据
* @param key
* @param <T>
* @return
* @throws Exception
*/
public <T> T getCache(final String key) throws Exception {
byte[] result = jedis.get(key.getBytes());
return (T) deserializeObj(result);
}

/**
* 根据key删除缓存数据
* @return
* @throws Exception
*/
public void delCache(final String key) throws Exception {
jedis.del(key.getBytes());
}

/**
* 序列化
* @param object
* @return
*/
private static byte[] serializeObj(Object object) {
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
byte[] bytes = baos.toByteArray();
return bytes;
} catch (Exception e) {
throw new RuntimeException("序列化失败!", e);
}
}

/**
* 反序列化
* @param bytes
* @return
*/
private static Object deserializeObj(byte[] bytes) {
if (bytes == null){
return null;
}
ByteArrayInputStream bais = null;
try {
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
throw new RuntimeException("反序列化失败!", e);
}
}

}

 

shiro的session管理器配置:

RedisSessionDAO.java:

package webapp.redis;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
* Created by Administrator on 2018/9/6.
*/
@Component
public class RedisSessionDAO extends EnterpriseCacheSessionDAO {
@Autowired
private RedisCache redisClusterCache;
/**
* 创建session,保存到redis数据库
*
* @param session
* @return
*/
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = super.doCreate(session);
try {
redisClusterCache.putCache(sessionId.toString(), session);
} catch (Exception e) {
e.printStackTrace();
}
return sessionId;
}
/**
* 获取session
*
* @param sessionId
* @return
*/
@Override
protected Session doReadSession(Serializable sessionId) {
// 先从缓存中获取session,如果没有再去数据库中获取
Session session = super.doReadSession(sessionId);
if (session == null) {
try {
session = redisClusterCache.getCache(sessionId.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
return session;
}
/**
* 更新session的最后一次访问时间
*
* @param session
*/
@Override
protected void doUpdate(Session session) {
super.doUpdate(session);
try {
redisClusterCache.putCache(session.getId().toString(), session);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除session
*
* @param session
*/
@Override
protected void doDelete(Session session) {
super.doDelete(session);
try {
redisClusterCache.delCache(session.getId().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}

 

shiro相关配置:

ShiroCoreController.java:

package webapp.shiro;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* Created by Administrator on 2018/9/5.
*/
@RestController
public class ShiroCoreController {
@GetMapping("/loginUnAuth")
public String loginUnAuth() {
return "{\n" +
"    \"codeEnum\": \"OVERTIME\",\n" +
"    \"code\": 0,\n" +
"    \"data\": null,\n" +
"    \"msg\": \"未登陆/登陆超时\",\n" +
"    \"success\": false\n" +
"}";
}
@GetMapping("/authorUnAuth")
public String authorUnAuth() {
return "{\n" +
"    \"codeEnum\": \"ERR_PERMISSIONS\",\n" +
"    \"code\": -2,\n" +
"    \"data\": null,\n" +
"    \"msg\": \"无此权限\",\n" +
"    \"success\": false\n" +
"}";
}
}

UserShiroRealm.java:

package webapp.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.springframework.beans.factory.annotation.Autowired;
import webapp.model.User;
import webapp.service.UserService;

import java.util.Collection;

/**
* Created by Administrator on 2018/9/5.
*/
public class UserShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private SessionDAO sessionDAO;

/**
* 角色权限和对应权限添加
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}

/**
* 用户认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//加这一步的目的是在Post请求的时候会先进认证,然后在到请求
if (authenticationToken.getPrincipal() == null) {
return null;
}

String userName = authenticationToken.getPrincipal().toString();

//只允许同一账户单个登录
Subject subject = SecurityUtils.getSubject();
Session nowSession = subject.getSession();
Collection<Session> sessions = sessionDAO.getActiveSessions();
if(sessions != null && sessions.size() > 0) {
for (Session session : sessions) {
if (!nowSession.getId().equals(session.getId()) && (session.getTimeout() == 0
|| userName.equals(String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY))))) {
sessionDAO.delete(session);
}
}
}

User user = userService.findOneByUserName(userName);
if (user == null) {
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
return new SimpleAuthenticationInfo(userName, user.getPassword(), getName());
}
}
}

ShiroConfig.java:

package webapp.conf;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import webapp.redis.RedisSessionDAO;
import webapp.shiro.UserShiroRealm;

import java.util.HashMap;

/**
* shiro配置类
* Created by Administrator on 2018/9/5.
*/
@Configuration
public class ShiroConfig {

//将自己的验证方式加入容器
@Bean
public UserShiroRealm userShiroRealm() {
return new UserShiroRealm();
}

@Bean
public RedisSessionDAO redisSessionDAO() {
return new RedisSessionDAO();
}

@Bean
public SimpleCookie getSimpleCookie() {
SimpleCookie simpleCookie = new SimpleCookie();
simpleCookie.setName("SHRIOSESSIONID");
return simpleCookie;
}

//配置shiro session 的一个管理器
@Bean(name = "sessionManager")
public DefaultWebSessionManager getDefaultWebSessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
sessionManager.setGlobalSessionTimeout(-1000);  //session有效期 默认值1800000 30分钟 1800000毫秒  -1000表示永久
SimpleCookie simpleCookie = getSimpleCookie();
simpleCookie.setHttpOnly(true);                 //设置js不可读取此Cookie
simpleCookie.setMaxAge(3 * 365 * 24 * 60 * 60); //3年 cookie有效期
sessionManager.setSessionIdCookie(simpleCookie);
return sessionManager;
}

//配置核心安全事务管理器
@Bean(name="securityManager")
public SecurityManager securityManager(@Qualifier("userShiroRealm") UserShiroRealm userShiroRealm,
@Qualifier("sessionManager") DefaultWebSessionManager sessionManager) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(userShiroRealm);
manager.setSessionManager(sessionManager);
return manager;
}

//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userShiroRealm());
return securityManager;
}

//加入注解的使用,不加入这个注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}

//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
HashMap<String, String> map = new HashMap<>();
//登出
//map.put("/logout", "logout");
//认证 /###/@@@/**
map.put("/api/**", "authc");

//登录认证不通过跳转
shiroFilterFactoryBean.setLoginUrl("/loginUnAuth");
//首页
//shiroFilterFactoryBean.setSuccessUrl("/index");
//权限认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/authorUnAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
} 

 

启动项目,访问相关接口校验是否成功:

登录接口:http://localhost:1001/core/user/login?userName=002&password=123456

  返回:002[42c6d423-e48e-4164-b17d-0cbc0f9ca832]

获取用户:http://localhost:1001/core/user/findUser

  返回:User(id=1, userName=007少侠, password=123456)

退出登录:http://localhost:1001/core/user/logout

  返回:退出登录成功

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: