shiro框架
shiro:强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理
Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心 脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会 话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实 现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的 哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可 以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储; 所以我们一般在应用中都需要实现自己的Realm;
SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个 组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境; 所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;
SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可 以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的 Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到 缓存中后可以提高访问的性能 Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
简单使用
认证:
1.根据配置文件创建SecurityManagerFactory
2.通过工厂获取SecurityManager
3.将SecurityManager绑定到当前运行环境
4.从当前运行环境中构造subject
5.构造shiro登录的数据
6.主体登陆
- 依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
- 配置文件
[users]
#用户名=密码,角色名
zhangsan=123456,role1,role2
lisi=123456,role2
[roles]
#角色
#角色名=权限列表
role1=user:save,user:update
role2=user:find
- 使用
package cn.itcast.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Before;
import org.junit.Test;
public class ShiroTest02 {
private SecurityManager securityManager;
@Before
public void init() {
//1.根据配置文件创建SecurityManagerFactory
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-test-2.ini");
//2.通过工厂获取SecurityManager
SecurityManager securityManager = factory.getInstance();
//3.将SecurityManager绑定到当前运行环境
SecurityUtils.setSecurityManager(securityManager);
}
@Test
public void testLogin() {
Subject subject = SecurityUtils.getSubject();
String username = "lisi";
String password = "123456";
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);
//登录成功之后,完成授权
//授权:检验当前登录用户是否具有操作权限,是否具有某个角色
System.out.println(subject.hasRole("role1")); // false
System.out.println(subject.isPermitted("user:save")); // false
}
}
realm实现自定义认证授权
- 配置文件
[main]
#声明realm
permReam=cn.itcast.shiro.PermissionRealm
#注册realm到securityManager中
securityManager.realms=$permReam
- 自定义realm
package cn.itcast.shiro;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义realms对象
* 继承AuthorizingRealm
* 重写方法
* doGetAuthorizationInfo:授权
* 获取到用户的授权数据(用户的权限数据)
* doGetAuthenticationInfo:认证
* 根据用户名密码登录,将用户数据保存(安全数据)
*
*/
public class PermissionRealm extends AuthorizingRealm {
/**
* 自定义realm名称
*/
public void setName(String name) {
super.setName("permissionRealm");
}
//授权:授权的主要目的就是根据认证数据获取到用户的权限信息
/**
* principalCollection:包含了所有已认证的安全数据
* AuthorizationInfoInfo:授权数据
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权方法");
//1.获取安全数据 username,用户id
String username = (String)principalCollection.getPrimaryPrincipal();
//2.根据id或者名称查询用户
//3.实际应从数据库查询用户的角色和权限信息,没有数据库,以下为构造数据
List<String> perms = new ArrayList<>();
perms.add("user:save");
perms.add("user:update");
List<String> roles = new ArrayList<>();
roles.add("role1");
roles.add("role2");
//4.构造返回
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//设置权限集合
info.addStringPermissions(perms);
//设置角色集合
info.addRoles(roles);
return info;
}
//认证:认证的主要目的,比较用户名和密码是否与数据库中的一致
//将安全数据存入到shiro进行保管
//参数:authenticationToken登录构造的usernamepasswordtoken
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证方法");
//1.构造uptoken
UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
//2.获取输入的用户名密码
String username = upToken.getUsername();
String password = new String(upToken.getPassword());
//3.根据用户名查询数据库,正式系统查询
//4.比较密码和数据库中的密码是否一致(密码可能需要加密)
if("123456".equals(password)) {
//5.如果成功,向shiro存入安全数据
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,getName());//1.安全数据,2.密码。3。当前realm域名称
return info;
}else{
//6.失败,抛出异常或返回null
throw new RuntimeException("用户名或密码错误");
}
}
}
- 测试
package cn.itcast.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Before;
import org.junit.Test;
public class ShiroTest03 {
private SecurityManager securityManager;
@Before
public void init() {
//1.根据配置文件创建SecurityManagerFactory
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-test-3.ini");
//2.通过工厂获取SecurityManager
SecurityManager securityManager = factory.getInstance();
//3.将SecurityManager绑定到当前运行环境
SecurityUtils.setSecurityManager(securityManager);
}
@Test
public void testLogin() {
Subject subject = SecurityUtils.getSubject();
String username = "zhangsan";
String password = "123456";
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//执行login-->realm域中的认证方法
subject.login(token);
//授权:-->realm域中的授权方法
System.out.println(subject.hasRole("role1"));
System.out.println(subject.isPermitted("user:save"));
}
}
springboot使用
- 依赖
<!--shiro与redis整合实现sessionDao -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.0.0</version>
</dependency>
<!--shiro和spring整合-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--shiro核心包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<!--shiro与redis整合-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.0.0</version>
</dependency>
- 配置
server:
port: 8081
spring:
application:
name: ihrm-company #指定服务名
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shiro_db?useUnicode=true&characterEncoding=utf8
username: root
password: root
jpa:
database: MySQL
show-sql: true
open-in-view: true
redis:
host: 127.0.0.1
port: 6379
- 自定义reaml实现认证鉴权
package cn.itcast.shiro.realm;
import cn.itcast.shiro.domain.Permission;
import cn.itcast.shiro.domain.Role;
import cn.itcast.shiro.domain.User;
import cn.itcast.shiro.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.Set;
public class CustomRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
public void setName(String name) {
super.setName("customRealm");
}
/**
* 授权方法
* 操作的时候,判断用户是否具有相应的权限
* 先认证 -- 安全数据
* 再授权 -- 根据安全数据获取用户具有的所有操作权限
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 1、获取已认证的用户数据
User user = (User) principalCollection.getPrimaryPrincipal();
// 2、根据用户数据获取用户的权限信息(所有角色,所有权限)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = new HashSet<>(); // 所有角色
Set<String> perms = new HashSet<>(); // 所有权限
for (Role role : user.getRoles()) {
roles.add(role.getName());
for (Permission perm : role.getPermissions()) {
perms.add(perm.getName());
}
}
info.setStringPermissions(perms);
info.setRoles(roles);
return info;
}
/**
* 认证方法
* 参数:传递的用户名和密码
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.获取登录的用户名密码(token)
UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
String username = upToken.getUsername();
String password = new String(upToken.getPassword());
//2.根据用户名查询数据库
User user = userService.findByName(username);
//3.判断用户是否存在或者密码是否一致
if (null != user && user.getPassword().equals(password)){
// 4.如果一致返回安全数据
// 构造方法:安全数据,密码,realm域名
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
return info;
}
//5.不一致,返回null(抛出异常)
return null;
}
}
- 添加配置类
package cn.itcast.shiro;
import cn.itcast.shiro.realm.CustomRealm;
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.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfiguration {
// 1-创建realm
@Bean
public CustomRealm getReaml(){
return new CustomRealm();
}
// 2-创建安全管理器
@Bean
public SecurityManager getSecurityManager(CustomRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
/**
* 3-配置shiro的过滤器工厂
* 在web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制.
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shirofilter(SecurityManager securityManager){
// 1-创建过滤器工厂
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
// 2-设置安全管理器
filterFactory.setSecurityManager(securityManager);
// 3-通用配置(登录页面,未授权跳转的页面)
filterFactory.setLoginUrl("/autherror?code=1"); // 跳转url
filterFactory.setUnauthorizedUrl("/autherror?code=2");
// 4-设置过滤器集合
/**
* 设置所有过滤器:有顺序的map
* key = 拦截的url地址
* value = 过滤器类型
*/
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/home","anon");//当前请求地址可以匿名访问
//具有某中权限才能访问
//使用过滤器的形式配置请求地址的依赖权限,不具备指定的权限,跳转到setUnauthorizedUrl地址
//filterMap.put("/user/home","perms[user-home]");
//使用过滤器的形式配置请求地址的依赖角色
//filterMap.put("/user/home","roles[系统管理员]");
filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问
filterFactory.setFilterChainDefinitionMap(filterMap);
return filterFactory;
}
// 开启对shior注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
- 测试controller
package cn.itcast.shiro.controller;
import cn.itcast.shiro.domain.User;
import cn.itcast.shiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
@RestController
public class UserController {
@Autowired
private UserService userService;
//个人主页
@RequestMapping(value = "/user/home")
public String home() {
return "访问个人主页成功!";
}
//添加
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String add() {
return "添加用户成功";
}
//查询
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String find() {
return "查询用户成功";
}
//更新
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
public String update(String id) {
return "更新用户成功";
}
//删除
@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
public String delete() {
return "删除用户成功";
}
//用户登录
@RequestMapping(value="/login")
public String login(String username,String password) {
System.out.println("用户登录");
try {
/**
* 密码加密:
* shiro提供的md加密
* 参数1:加密内容
* 111111 --- abcd
* 参数2:盐(加密的混淆字符串) 使用登录用户名
* 111111+混淆字符串
* 参数3:加密次数 3
*/
password = new Md5Hash(password, username, 3).toString();
// 1.构造登录令牌
UsernamePasswordToken upToken = new UsernamePasswordToken(username, password);
// 2、获取subject
Subject subject = SecurityUtils.getSubject();
// 3、调用subject进行登录
subject.login(upToken);
return "登录成功";
}catch (Exception e){
e.printStackTrace();
return "用户名或密码错误!";
}
}
@RequestMapping(value = "/autherror",method = RequestMethod.GET)
public String autherror(int code) {
return code==1?"未登录":"未授权";
}
}
shiro注解使用
配置类中需要添加开启注解的配置。
- 开启
// 开启对shior注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
- controller测试方法
/**
* 说明:
* @RequiresPermissions() --访问此方法需要的权限
* @RequiresRoles() --访问此方法需要的角色
*
* 过滤器鉴权:权限不匹配跳转到通过setUnauthorizedUrl()设置的地址
* 注解:权限不匹配则抛出异常【AuthorizationExceptior】
*/
//个人主页
@RequiresPermissions("user-home")
@RequiresRoles("system-admin")
@RequestMapping(value = "/user/home")
public String home() {
return "访问个人主页成功!";
}
shiro会话管理
在shiro里所有的用户的会话信息都会由Shiro来进行控制,shiro提供的会话可以用于JavaSE/JavaEE环境,不依赖于任何底层容器,可以独立使用,是完整的会话模块。通过Shiro的会话管理器(SessionManager )进行统一的会话管理。
SessionManager(会话管理器)∶管理所有Subject的session包括创建、维护、删除、失效、验证等工作。SessionManager是顶层组件,由SecurityManager管理。shiro提供了三个默认实现:
- DefaultSessionManager:用于JavaSE环境
- ServletContainerSessionManager:用于Web环境,直接使用servlet容器的会话。【默认使用】
- DefaultWebSessionManager:用于web环境,自己维护会话(自己维护着会话,直接废弃了Servlet容器的会话管理,即session可根据自己需要指定存储位置)。
测试默认session存储:
//登录成功后,打印所有session内容
@RequestMapping(value="/show")
public String show(HttpSession session) {
// 获取session中所有的键值
Enumeration<?> enumeration = session.getAttributeNames();
// 遍历enumeration中的
while (enumeration.hasMoreElements()) {
// 获取session键值
String name = enumeration.nextElement().toString();
// 根据键值取session中的值
Object value = session.getAttribute(name);
// 打印结果
System.out.println("<B>" + name + "</B>=" + value + "<br>/n");
}
return "查看session成功";
}
应用场景
- 通过认证中心把信息存入redis并返回sessionid
- 访问其余服务带上sessionid(放到请求头或coockie中),服务通过会话管理区redis中获取信息,完成认证。
使用readis存储会话
依据上面springboot使用例子。
- 依赖
<!--shiro与redis整合-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.0.0</version>
</dependency>
- 配置文件指定redis地址
spring:
redis:
host: 127.0.0.1
port: 6379
- 定义会话管理器
package cn.itcast.shiro.session;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
/**
* @Author
* @Date 2021-08-10 00:00
*/
public class CustomSessionManager extends DefaultWebSessionManager {
/**
* sessionid通过头信息携带:
* 通过请求头获取:Authorization:sessionid
* 指定sessionId的获取方式
*
* @param request
* @param response
* @return
*/
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
// 获取请求头Authorization数据
String id = WebUtils.toHttp(request).getHeader("Authorization");
if (StringUtils.isEmpty(id)){
// 没有携带,生成新sessionId
return super.getSessionId(request,response);
}else {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
}
}
}
- shiro配置类中
package cn.itcast.shiro;
import cn.itcast.shiro.realm.CustomRealm;
import cn.itcast.shiro.session.CustomSessionManager;
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.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Author
* @Date 2021-08-09 00:24
*/
@Configuration
public class ShiroConfiguration {
// 1-创建realm
@Bean
public CustomRealm getReaml(){
return new CustomRealm();
}
// 2-创建安全管理器
@Bean
public SecurityManager getSecurityManager(CustomRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
// 将自定义会话管理器告知安全管理器
securityManager.setSessionManager(sessionManager());
// 将自定义redis缓存管理器注册到安全管理器
securityManager.setCacheManager(cacheManager());
return securityManager;
}
// 3-配置shiro的过滤器工厂
/**
* 在web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制.
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shirofilter(SecurityManager securityManager){
// 1-创建过滤器工厂
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
// 2-设置安全管理器
filterFactory.setSecurityManager(securityManager);
// 3-通用配置(登录页面,未授权跳转的页面)
filterFactory.setLoginUrl("/autherror?code=1"); // 跳转url
filterFactory.setUnauthorizedUrl("/autherror?code=2");
// 4-设置过滤器集合
/**
* 设置所有过滤器:有顺序的map
* key = 拦截的url地址
* value = 过滤器类型
*/
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/home","anon");//当前请求地址可以匿名访问
//具有某中权限才能访问
//使用过滤器的形式配置请求地址的依赖权限
//filterMap.put("/user/home","perms[user-home]"); //不具备指定的权限,跳转到setUnauthorizedUrl地址
//使用过滤器的形式配置请求地址的依赖角色
//filterMap.put("/user/home","roles[系统管理员]");
filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问
filterFactory.setFilterChainDefinitionMap(filterMap);
return filterFactory;
}
// 开启对shior注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
/**
* 1-redis控制器,操作redis
*/
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
return redisManager;
}
/**
* 2-sessionDao
*/
public RedisSessionDAO redisSessionDAO(){
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
return sessionDAO;
}
/**
* 3-会话管理器
*/
public DefaultWebSessionManager sessionManager(){
CustomSessionManager sessionManager = new CustomSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/**
* 4-缓存管理器
*/
public RedisCacheManager cacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* 5-将自定义会话管理器告知安全管理器(查看上面安全管理器设置)
*/
}
- 存储的安全数据需要实现接口:AuthCachePrincipal (redis和shiro插件包提供的接口,使安全数据可以存到redis中)
package cn.itcast.shiro.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.crazycake.shiro.AuthCachePrincipal;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
/**
* 用户实体类
*/
@Entity
@Table(name = "pe_user")
@Getter
@Setter
public class User implements Serializable, AuthCachePrincipal {
private static final long serialVersionUID = 4297464181093070302L;
/**
* ID
*/
@Id
private String id;
private String username;
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name="pe_user_role",joinColumns={@JoinColumn(name="user_id",referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}
)
private Set<Role> roles = new HashSet<Role>();//用户与角色 多对多
@Override
public String getAuthCacheKey() {
return null;
}
}
- controller测试
//用户登录
@RequestMapping(value="/login",method = RequestMethod.GET)
public String login(String username,String password) {
System.out.println("用户登录");
try {
/**
* 密码加密:
* shiro提供的md加密
* 参数1:加密内容
* 111111 --- abcd
* 参数2:盐(加密的混淆字符串) 使用登录用户名
* 111111+混淆字符串
* 参数3:加密次数 3
*/
password = new Md5Hash(password, username, 3).toString();
// 1.构造登录令牌
UsernamePasswordToken upToken = new UsernamePasswordToken(username, password);
// 2、获取subject
Subject subject = SecurityUtils.getSubject();
// 获取session
String sid = (String) subject.getSession().getId();
// 3、调用subject进行登录
subject.login(upToken);
return "登录成功:"+sid;
}catch (Exception e){
e.printStackTrace();
return "用户名或密码错误!";
}
}
浏览器和postman属于两个不同的会话。如何实现登录认证?
浏览器访问【/login】接口返回登录的sessionid,在postman中直接添加请求头:【Authorization:sessionid】 访问相关接口如:【/user】即可正常访问,实现了登录认证。