Shiro
- Apache Shiro 是一个java安全(权限)的框架
- Shiro 可以非常容易的开发出足够好的应用,其不仅可以使用JavaSE环境,也可以用在javaEE环境
- Shiro可以完成,认证,授权,加密管理,Web基础,缓存等
- 下载地址https://shiro.apache.org
Shiro核心三大对象
Subject | 用户 |
---|---|
SecurityManager | 管理所有用户 |
Realm | 策略认证 |
三种策略认证
AuthenticationStrategy.class | 描述 |
---|---|
AtLeastOneSuccessfulStrategy | 只要有一个(或更多)的Realm验证成功,那么认证将视为成功 |
FirstSuccessfulStrategy | 第一个Realm验证成功,整体认证将视为成功,且后续Realm将被忽略 |
AllSuccessfulStrategy | 所有的Realm成功,认证才视为成功 |
Quickstart核心:
//获取当前用户对象 Subject
Subject currentUser = SecurityUtils.getSubject();
//通过当前用户 获取获取session
Session session = currentUser.getSession();
//判断用户是否认证
if (!currentUser.isAuthenticated()) {
// 获取当前用户的认证 存取信息
currentUser.getPrincipal()
//判断用户是否有某个角色
if (currentUser.hasRole("schwartz")) {
//检测你是否有什么样的权限
if (currentUser.isPermitted("lightsaber:wield")) {
//注销
currentUser.logout();
1.官方Quickstart案例
package com;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
//import org.apache.shiro.ini.IniSecurityManagerFactory;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
//import org.apache.shiro.lang.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Simple Quickstart application showing how to use Shiro's API.
*
* @since 0.9 RC2
*/
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
//新方法 shiro更新问题,获取ini
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
SecurityUtils.setSecurityManager(securityManager);
//获取当前的用户对象subject
Subject currentUser = SecurityUtils.getSubject();
//通过当前用户拿到session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
//判断当前的用户是否被认证
if (!currentUser.isAuthenticated()) {
//token:令牌,没有获取,随机
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);//设置记住我
try {
currentUser.login(token);//执行登录操作
} catch (UnknownAccountException uae) {//用户名不存在
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {//密码不正确
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {//用户没锁定
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//获取用户信息
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//获取角色
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//粗粒度
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//细粒度
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//注销
//all done - log out!
currentUser.logout();
//结束
System.exit(0);
}
}
2.测试SpringBoot,Shiro认证
导入jar包
<!--
Subject 用户
SecurityManager 管理所有用户
Realm 连接数据
-->
<!-- shiro整合spring的包-->
<!-- Shiro整合Spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
编写一个controller并写出带有thymeleaf的html
package com.demo.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping({"/","/idnex"})
public String toIndex(Model model){
model.addAttribute("msg","hello,Shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/login")
public String login(String username,String password,Model model){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
System.out.println(username+password+"1111");
//获取用户登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try{
subject.login(token);//执行登录方法,如果没有异常就OK了
return "index";
}
catch (UnknownAccountException e){//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}
catch (IncorrectCredentialsException e){//密码不存在
model.addAttribute("msg","密码不错误");
return "login";
}
}
}
login.html:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<p th:text="${msg}" style="color: red"> </p>
<form th:action="@{/login}">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password" ></p>
<p><input type=submit></p>
</form>
</body>
</html>
Index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<hr>
<a th:href="@{/user/add}">add</a>|
<a th:href="@{/user/update}">update</a>
</body>
</html>
user/add 与user/update
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>
=================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
配置shiroConfig
@Configuration
public class ShiroConfig {
/**
* @param defaultWebSecurityManager
* @return
*/
@Bean
//ShiroFilterFactoryBean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/**
* anon:无需认证就可以访问
* authc:必须认证才能访问
* user:必须拥有记住我功能才能用
* perms:拥有对某个资源的全选才能访问
* role:拥有某个角色才能访问
*/
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/user/add","authc");
filterChainDefinitionMap.put("/user/update","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置登陆界面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,自定义:===>第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
实现UserRealm
package com.demo.config;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("授权===>doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证===>doGetAuthorizationInfo");
//用户名,密码
String name = "root";
String password = "123456";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
if(!userToken.getUsername().equals(name)){
return null; //UnknownAccountException
}
//密码认证,shiro做
return new SimpleAuthenticationInfo("",password,"");
}
}
这个demo确实可以做到拦截功能但是并没有对用户功能进行分类,因此改进成下面的demo
3.测试数据库Shiro授权
创建一个数据库
DROP TABLE IF EXISTS `user`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `user` (
`id` int NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
`perms` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `user`
--
LOCK TABLES `user` WRITE;
/*!40000 ALTER TABLE `user` DISABLE KEYS */;
INSERT INTO `user` VALUES (1,'张三','123123','user:update'),(2,'李四','123456',NULL),(3,'王五','123456',NULL),(4,'赵六','123456',NULL),(6,'找牛','1221212',NULL),(7,'admin','123456','user:add');
/*!40000 ALTER TABLE `user` ENABLE KEYS */;
UNLOCK TABLES;
导入数据库的依赖以及druid链接池
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
数据库配置
spring:
datasource:
username: root
password: root1@12
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
# 指定数据源
type: com.alibaba.druid.pool.DruidDataSource
# 数据源其他配置
druid:
#SpringBoot默认是不注入这些的,需要自己绑定
#druid数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity
#则导入log4j 依赖就行
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis配置
mybatis.type-aliases-package=com.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
修改index界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录</a>
</div>
<hr>
<div shiro:hasPermission="'user:add'">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="'user:update'">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
写一个简单的未认证界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>unauthorization</h1>
</body>
</html>
配置数据库实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
private String perms;
}
写一个mapper接口
@Repository
@Mapper
public interface UserMapper {
public User queryUserByName(String name);
}
实现:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应对Dao接口-->
<mapper namespace="com.mapper.UserMapper">
<select id="queryUserByName" parameterType="String" resultType="User">
select * from mybatis.user where name=#{name}
</select>
</mapper>
service接口
public interface UserService {
public User queryUserByName(String name);
}
实现:
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
重写controller
package com.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping({"/","/idnex"})
public String toIndex(Model model){
model.addAttribute("msg","hello,Shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/login")
public String login(String username,String password,Model model){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//获取用户登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try{
subject.login(token);//执行登录方法,如果没有异常就OK了
return "index";
}
catch (UnknownAccountException e){//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}
catch (IncorrectCredentialsException e){//密码不存在
model.addAttribute("msg","密码不错误");
return "login";
}
}
@RequestMapping("/noauth")
public String unantuorized(){
return "unauthorization";
}
}
重写shiroConfig
package com.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
//ShiroFilterFactoryBean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/**
*
* anon:无需认证就可以访问
* authc:必须认证才能访问
* user:必须拥有记住我功能才能用
* perms:拥有对某个资源的全选才能访问
* role:拥有某个角色才能访问
*/
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
//授权,正常情况下,没有授权会跳转到为授权页面
filterChainDefinitionMap.put("/user/add","perms[user:add]");
filterChainDefinitionMap.put("/user/update","perms[user:update]");
//拦截
filterChainDefinitionMap.put("/user/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置登陆界面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//设置为授权的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
return shiroFilterFactoryBean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,自定义:===>第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
@Bean
//整合shiroDialect;用来整合shiro thymeleof
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
重新实现UserRealm
package com.config;
import com.pojo.User;
import com.service.UserService;
import org.apache.shiro.SecurityUtils;
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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collections;
//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("授权===>doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addStringPermission("user:add");
//拿到当前登录的对象
Subject subject = SecurityUtils.getSubject();
//拿到User对象
User currentUser = (User) subject.getPrincipal();
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证===>doGetAuthorizationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//连接数据库
User user = userService.queryUserByName(userToken.getUsername());
if (user==null){
//UnknownAccountException
return null;
}
Subject current = SecurityUtils.getSubject();
Session session = current.getSession();
session.setAttribute("loginUser",user);
//可以加密,MD5,MD5盐值加密(MD5值后面加上一些用户的属性)
//密码认证,shiro做
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
测试效果:admin不能进入update页面
4.自定义登录认证
- 导入maven依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.1</version>
</dependency>
-
配置ini文件
[users] zhangsan=z3,role1,role2 lisi=l4 [roles] role1=user:insert,user:select
-
测试MD5加密
public class ShiroMD5 { public static void main(String[] args) { //密码明文 String password = "z3"; //使用MD5 加密 Md5Hash md5Hash = new Md5Hash(password); System.out.println(md5Hash); //带盐的md5加密,就是拼接新字符串 Md5Hash md5Hash2 = new Md5Hash(password,"salt"); System.out.println(md5Hash2.toHex()); //Md5 迭代加密 Md5Hash md5Hash3 = new Md5Hash(password,"salt",3); System.out.println(md5Hash3); //使用父类进行加密 SimpleHash simpleHash = new SimpleHash("MD5",password,"salt",3); System.out.println(simpleHash); } }
输出
a61d1457beb4684e254ce60379c8ae7b dd4611daf1e40eff99b9fdcadbd22674 7174f64b13022acd3c56e2781e098a5f 7174f64b13022acd3c56e2781e098a5f
-
编写测试类
package com; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import sun.net.www.protocol.http.AuthenticationInfo; public class TestShiroRun { public static void main(String[] args) { //1. 初始化获取SecurityManager IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //2. 获取Subject对象 Subject subject = SecurityUtils.getSubject(); //3. 创建token对象,web应用用户名密码从页面传递 AuthenticationToken token = new UsernamePasswordToken("zhangsan","z3"); //4. 完成登录 try { subject.login(token); System.out.println("登录成功"); //5.判断角色 boolean role1 = subject.hasRole("role1"); System.out.println("是否拥有此角色:"+role1); //6.判断权限 boolean permitted = subject.isPermitted("user:insert"); System.out.println("是否用于此权限: "+permitted); //也可以用checkPermission方法,但没有返回值,没有权限会抛异常 //subject.checkPermission(""); }catch (UnknownAccountException e){ System.out.println("用户不存在"); }catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } } }
输出
认证用户信息:zhangsan-----+z3 登录成功 是否拥有此角色: true 是否用于此权限: false
-
实现自定义类 ,继承
AuthorizingRealm
接口package com; 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.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; /** * @自定义登录认证方法,shiro的login方法的底层会调用该类的认证方法进行认证 * @需要配置自定义的realm生效,在ini文件中配置,在springboot中配置 * @该方法知识获取进行对比的信息,认证逻辑还是按照shiro的底层认证逻辑完成 */ public class MyRealm extends AuthorizingRealm { //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //1. 获取身份信息 String principal = token.getPrincipal().toString(); //2. 获取凭证信息 String password = new String((char[]) token.getCredentials()); System.out.println("认证用户信息:"+principal+"-----+"+password); //3. 获取数据库中存储的用户信息 if(principal.equals("zhangsan")){ //3.1 数据库中存储的加盐三次迭代的密码 String pwdInfo = "7174f64b13022acd3c56e2781e098a5f"; //4. 创建封装校验逻辑对象,封装数据返回 AuthenticationInfo info = new SimpleAuthenticationInfo( token.getPrincipal(), pwdInfo, ByteSource.Util.bytes("salt"), token.getPrincipal().toString() ); return info; } return null; } }
-
重新配置ini文件
[users]
zhangsan=7174f64b13022acd3c56e2781e098a5f,role1,role2
lisi=l4
[roles]
role1=user:insert,user:select
[main]
md5CredentialsMatcher=org.apache.shiro.authc.credential.Md5CredentialsMatcher
md5CredentialsMatcher.hashIterations=3
myrealm=com.MyRealm
myrealm.credentialsMatcher=$md5CredentialsMatcher
securityManager.realms=$myrealm
输出
认证用户信息:zhangsan-----+z3
登录成功
是否拥有此角色:false
是否用于此权限: false
5.Ehcaher缓存管理
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
1. ehcache 和 redis 比较
ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。
redis是通过socket访问到缓存服务,效率比Ehcache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。如果是单个应用或者对缓存访问要求很高的应用,用ehcache。如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用redis。
ehcache也有缓存共享方案,不过是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适。
2.测试案例
导入maven依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.6.11</version>
<type>pom</type>
</dependency>
配置xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache >
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir/ehcache"/>
<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!-- cache 可以设置多个,例如localCache、UserCache和HelloWorldCache -->
<cache name="localCache"
eternal="true"
maxElementsInMemory="100"
maxElementsOnDisk="1000"
overflowToDisk="true"
diskPersistent="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
memoryStoreEvictionPolicy="LRU"/>
<cache name="UserCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<!-- hello world缓存 -->
<cache name="HelloWorldCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<!-- memoryStoreEvictionPolicy Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)-->
<!-- 缓存配置
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk
store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
</ehcache>
编写测试类
public class Main {
public static void main(String[] args) {
// 1. 创建缓存管理器
CacheManager cacheManager = CacheManager.create(args.getClass().getResourceAsStream("/ehcache.xml"));
// 2. 获取ehcache.xml 中定义的 HelloWorldCache 缓存对象
Cache cache = cacheManager.getCache("HelloWorldCache");
// 3. 创建元素
Element element = new Element("name", "张三");
// 4. 将元素添加到缓存
cache.put(element);
// 5. 获取缓存
Element value = cache.get("name");
System.out.println(value);
System.out.println(value.getObjectKey());
System.out.println(value.getObjectValue());
// 6. 删除元素
cache.remove("name");
System.out.println(cache.getSize());
// 7. 刷新缓存
cache.flush();
// 8. 关闭缓存管理器
cacheManager.shutdown();
}
}
输出:
[ key = name, value=张三, version=1, hitCount=1, CreationTime = 1664696940423, LastAccessTime = 1664696940424 ]
name
张三
0
6.整合SpringBoot,Shiro,Ehcache
实现功能:不同的用户有不同的的权限,页面的显示效果不一样,登录会进行认证,数据库的密码为MD5 加盐迭代密码,有记住我功能,有缓存功能,有异常处理的功能
1、创建数数据库shiro_db
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for permissions
-- ----------------------------
DROP TABLE IF EXISTS `permissions`;
CREATE TABLE `permissions` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` varchar(30) DEFAULT NULL COMMENT '权限名',
`info` varchar(30) DEFAULT NULL COMMENT '权限信息',
`desc` varchar(40) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COMMENT='权限表';
-- ----------------------------
-- Records of permissions
-- ----------------------------
BEGIN;
INSERT INTO `permissions` (`id`, `name`, `info`, `desc`) VALUES (1, '删除用户', 'user:delete', '删除用户');
INSERT INTO `permissions` (`id`, `name`, `info`, `desc`) VALUES (2, '新增用户', 'user:add', '新增用户');
INSERT INTO `permissions` (`id`, `name`, `info`, `desc`) VALUES (3, '修改用户', 'user:edit', '修改用户');
COMMIT;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` bigint NOT NULL COMMENT '编号',
`name` varchar(30) DEFAULT NULL COMMENT '角色名',
`desc` varchar(50) DEFAULT NULL COMMENT '描述',
`realname` varchar(50) DEFAULT NULL COMMENT '显示角色名'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of role
-- ----------------------------
BEGIN;
INSERT INTO `role` (`id`, `name`, `desc`, `realname`) VALUES (1, 'admin', '所有权限', '管理员');
INSERT INTO `role` (`id`, `name`, `desc`, `realname`) VALUES (2, 'userMag', '用户管理权限', '用户管理');
COMMIT;
-- ----------------------------
-- Table structure for role_ps
-- ----------------------------
DROP TABLE IF EXISTS `role_ps`;
CREATE TABLE `role_ps` (
`id` bigint NOT NULL AUTO_INCREMENT,
`rid` bigint DEFAULT NULL,
`pid` bigint DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of role_ps
-- ----------------------------
BEGIN;
INSERT INTO `role_ps` (`id`, `rid`, `pid`) VALUES (1, 1, 1);
INSERT INTO `role_ps` (`id`, `rid`, `pid`) VALUES (2, 1, 2);
INSERT INTO `role_ps` (`id`, `rid`, `pid`) VALUES (3, 1, 3);
COMMIT;
-- ----------------------------
-- Table structure for role_user
-- ----------------------------
DROP TABLE IF EXISTS `role_user`;
CREATE TABLE `role_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`uid` bigint DEFAULT NULL COMMENT '用户id',
`rid` bigint DEFAULT NULL COMMENT '角色id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of role_user
-- ----------------------------
BEGIN;
INSERT INTO `role_user` (`id`, `uid`, `rid`) VALUES (1, 1, 1);
INSERT INTO `role_user` (`id`, `uid`, `rid`) VALUES (2, 1, 2);
INSERT INTO `role_user` (`id`, `uid`, `rid`) VALUES (3, 2, 2);
COMMIT;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` varchar(30) DEFAULT NULL COMMENT '用户名',
`pwd` varchar(50) DEFAULT NULL COMMENT '密码',
`rid` bigint DEFAULT NULL COMMENT '角色编码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` (`id`, `name`, `pwd`, `rid`) VALUES (1, '张三', '7174f64b13022acd3c56e2781e098a5f', NULL);
INSERT INTO `user` (`id`, `name`, `pwd`, `rid`) VALUES (2, '李四', '7174f64b13022acd3c56e2781e098a5f', NULL);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
2、导入Mvean依赖
- thymeleaf
- springboot_web
- mysql数据库连接jar包
- lombok
- test
- junit
- springboot_mybatis_plus
- springboot_shiro
- thymeleaf_shiro
- shiro_ehcache
- commons_io
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
<!--Shiro整合Ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
3、创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
private Integer rid;
}
4、创建Mapper接口
-
CRUD自动实现,继承BaseMapper
-
获取用户的拥有的角色信息
getUserRoleInfoMapper
-
获取返回过来的角色权限信息
getUserPermissionInfo
@Repository
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT name FROM role WHERE id IN ( SELECT rid FROM role_user WHERE uid =( SELECT id FROM user WHERE NAME=#{principal}))")
List<String> getUserRoleInfoMapper(@Param("principal")String principal);
/*
* foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。
foreach元素的属性主要有 item,index,collection,open,separator,close。
item集合中每一个元素进行迭代时的别名,
index表示在迭代过程中,每次迭代到的位置,
open该语句以什么开始,
separator在每次进行迭代之间以什么符号作为分隔 符,
close以什么结束,
在使用foreach的时候最关键的也是最容易出错的就是collection属性,
该属性是必须指定的,但是在不同情况 下,该属性的值是不一样的,
主要有一下3种情况:
1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了
* */
@Select(
{
"<script>",
"select info from permissions where id In",
"(select pid from role_ps where rid in (",
"select id from role where name in",
"<foreach collection = 'roles' item = 'name' open ='(' separator = ',' close = ')' >" ,
"#{name}",
"</foreach>",
"))",
"</script>"
}
)
List<String> getUserPermissionInfoMapper(@Param("roles") List<String> roles);
}
5、Service接口与实现
public interface UserService {
//用户登录
User getUserInfoByName(String name);
//根据用户查询角色信息
List<String> getUserRoleInfo(String principal);
//获取用户角色权限信息
List<String> getUserPermissionInfo(List<String> roles);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserInfoByName(String name) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name",name);
User user = userMapper.selectOne(wrapper);
return user;
}
//根据用户查询角色信息
@Override
public List<String> getUserRoleInfo(String principal) {
return userMapper.getUserRoleInfoMapper(principal);
}
//获取用户角色信息
@Override
public List<String> getUserPermissionInfo(List<String> roles) {
return userMapper.getUserPermissionInfoMapper(roles);
}
}
6、配置文件
applicaton.yml
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shiro_db?useUnicode=true&characterEncoding=utf8&useSSL=true
#&serverTimezone=UTC
password: root1@12
username: root
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
shiro:
loginUrl: /myController/login
Ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache >
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir/ehcache"/>
<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!-- cache 可以设置多个,例如localCache、UserCache和HelloWorldCache -->
<cache name="localCache"
eternal="true"
maxElementsInMemory="100"
maxElementsOnDisk="1000"
overflowToDisk="true"
diskPersistent="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
memoryStoreEvictionPolicy="LRU"/>
<cache name="UserCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<!-- hello world缓存 -->
<cache name="HelloWorldCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<!-- memoryStoreEvictionPolicy Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)-->
<!-- 缓存配置
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk
store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
</ehcache>
7、自定义登录授权与认证
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//自定义授权方法:获取当前登录用户的角色、权限信息、返回给shiro用来进行授权认证
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("自定义授权方法==>");
/*
//1. 创建对象,封装当前登录用户的角色,和权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//2. 植入角色【模式数据库测试】
info.addRole("admin");
//3. 返回信息
return info;
*/
//1. 获取用户身份信息
String principal = principals.getPrimaryPrincipal().toString();
//2.1 调用业务层获取用户的角色信息【数据库】
List<String> userRoleInfo = userService.getUserRoleInfo(principal);
System.out.println("当前用户角色信息 = " + userRoleInfo);
//2.2 调用业务层获取用户的权限信息(数据库)
List<String> userPermissionInfo = userService.getUserPermissionInfo(userRoleInfo);
System.out.println("用户的权限信息 = " + userPermissionInfo);
//3. 创建对象,封装当前登录角色、权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(userRoleInfo);
info.addStringPermissions(userPermissionInfo);
//4. 返回信息
return info;
}
//自定以登录认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. 获取用户身份信息
String name = token.getPrincipal().toString();
// 2. 调用业务层获取用户
User user = userService.getUserInfoByName(name);
// 3. 非空判断,将数据封装返回
if(user != null){
AuthenticationInfo info = new SimpleAuthenticationInfo(
token.getPrincipal(),
user.getPwd(),
ByteSource.Util.bytes("salt"),
token.getPrincipal().toString()
);
return info;
}
return null;
}
}
8、ShiroConfig
package com.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import com.realm.MyRealm;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.io.InputStream;
@Configuration
public class ShiroConfig {
@Autowired
private MyRealm myRealm;
//配置SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//1. 创建defaultWebSecurityManager对象
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//2. 创建加密对象,并设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//2.1 采用md5加密
matcher.setHashAlgorithmName("md5");
//2.2 采用迭代加密到次数
matcher.setHashIterations(3);
//3. 将加密对象存储到myRealm中
myRealm.setCredentialsMatcher(matcher);
//4.1 将myRealm对象存入到defaultWebSecurityManager对象中
defaultWebSecurityManager.setRealm(myRealm);
//4.2 设置rememberMe
defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
//4.3 设置缓存容器
defaultWebSecurityManager.setCacheManager(getEhcacheManager());
//5. 返回
return defaultWebSecurityManager;
}
//cookie缓存管理器
public EhCacheManager getEhcacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
InputStream is = null;
try {
is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");
} catch (IOException e) {
e.printStackTrace();
}
CacheManager cacheManager = new CacheManager(is);
ehCacheManager.setCacheManager(cacheManager);
return ehCacheManager;
}
//cookie属性设置
public SimpleCookie rememberMeCookie(){
SimpleCookie cookie = new SimpleCookie("rememberMe");
//设置跨域
//cookie.setDomain(domain);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(30*24*60*60);
return cookie;
}
//创建Shirode的cookie管理对象
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
return cookieRememberMeManager;
}
//配置Shiro内置过滤器拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition = new DefaultShiroFilterChainDefinition();
/**
*
* anon:无需认证就可以访问
* authc:必须认证才能访问
* user:必须拥有记住我功能才能用
* perms:拥有对某个资源的全选才能访问
* role:拥有某个角色才能访问
* logout:登出
*/
//设置不认证可以反问的资源
defaultShiroFilterChainDefinition.addPathDefinition("/myController/userLogin","anon");
defaultShiroFilterChainDefinition.addPathDefinition("/myController/login","anon");
//添加登出的过滤器
defaultShiroFilterChainDefinition.addPathDefinition("/logout","logout");
//设置需要进行登录认证的拦截范围
defaultShiroFilterChainDefinition.addPathDefinition("/**","authc");
//添加存在用户的过滤器(rememberMe)
defaultShiroFilterChainDefinition.addPathDefinition("/**","user");
return defaultShiroFilterChainDefinition;
}
//thymeleaf解析shiro
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
9、配置Controller
@Controller
@RequestMapping("myController")
public class MyController {
//跳转到登录页面
@GetMapping("login")
public String login(){
return "login";
}
//登录认证
@GetMapping("userLogin")
public String userLogin(String name, String pwd, @RequestParam(defaultValue = "false")boolean rememberMe, HttpSession session){
//1. 获取subject对象
Subject subject = SecurityUtils.getSubject();
//2. 封装请求数据到token
AuthenticationToken token = new UsernamePasswordToken(name,pwd,rememberMe);
//3. 调用Login方法进行登录认证
try {
subject.login(token);
session.setAttribute("user",token.getPrincipal().toString());
return "main";
}catch (AuthenticationException exception){
exception.printStackTrace();
System.out.println("登录失败!");
return "登录失败";
}
}
//登录认证验证rememberMe
@GetMapping("userLoginRm")
public String userLogin(HttpSession session){
session.setAttribute("user","rememberMe");
return "main";
}
//登录认证验证角色
@RequiresRoles("admin")
@GetMapping("userLoginRoles")
@ResponseBody
public String userLoginRoles(){
System.out.println("登录认证验证角色");
return "验证角色成功";
}
//登录认证验证权限
@RequiresPermissions("user:delete")
@GetMapping("userPermissions")
@ResponseBody
public String userLoginPermissions(){
System.out.println("登录认证验证权限");
return "验证权限成功";
}
}
10、异常处理
@ControllerAdvice
public class PermissionsException {
@ResponseBody
@ExceptionHandler(UnauthorizedException.class)
public String unauthorizedException(Exception e){
return "没有权限";
}
@ResponseBody
@ExceptionHandler(AuthorizationException.class)
public String authorizationException(Exception e){
return "权限认证失败";
}
}
11、前端页面
登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证</h1>
<br>
<form action="/myController/userLogin">
<div>用户名:<input type="text" name="name" value=""></div>
<div>密码:<input type="password" name="pwd" value=""></div>
<div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>
<div><input type="submit" value="登录"></div>
</form>
</body>
</html>
主页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证后的主页</h1>
<br>
登录用户为:<span th:text="${session.user}"></span>
<br>
<a href="/logout">登出</a>
<br>
<a shiro:hasRole="admin" href="/myController/userLoginRoles">测试授权--角色验证</a>
<a shiro:hasPermission="user:delete" href="/myController/userPermissions">测试授权--权限验证</a>
</body>
</html>
标签:Shiro,user,import,apache,org,public,shiro
From: https://www.cnblogs.com/xiangsir/p/16749138.html