目录
1.Shiro框架简介
DEMO 下载
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Apache Shiro 体系结构
1、Authentication 认证 ---- 用户登录
2、Authorization 授权 — 用户具有哪些权限
3、Cryptography 安全数据加密
4、Session Management 会话管理
5、Web Integration web系统集成
6、Interations 集成其它应用,spring、缓存框架
2.Spring Boot构架
1、选择springboot项目
2、pom:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<artifactId>springboot-shiro</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- shiro与spring整合依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- thymel对shiro的扩展坐标 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 导入mybatis相关的依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- SpringBoot的Mybatis启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.1自定义Realm类
package com.goolecloud.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* 自定义Realm
* @author
*
*/
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行认证逻辑");
return null;
}
}
2.2编写Shiro配置类(*)
package com.goolecloud.config;
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;
/**
* Shiro的配置类
*
*
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name="userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
2.3.使用Shiro内置过滤器实现页面拦截
package com.goolecloud.config;
import java.util.LinkedHashMap;
import java.util.Map;
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;
/**
* Shiro的配置类
* @author
*
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<String,String>();
/*filterMap.put("/add", "authc");
filterMap.put("/update", "authc");*/
filterMap.put("/testThymeleaf", "anon");
filterMap.put("/*", "authc");
//修改调整的登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name="userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
2.4实现用户认证(登录)操作
2.4.1设计登录页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h3>登录</h3>
<form method="post" action="login">
用户名:<input type="text" name="name"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
2.4.2编写Controller的登录逻辑
/**
* 登录逻辑处理
*/
@RequestMapping("/login")
public String login(String name,String password,Model model){
System.out.println("name"+name);
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
//跳转到test.html
return "redirect:/testThymeleaf";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败:密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
2.4.3编写Realm的判断逻辑
package com.goolecloud.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.authc.UsernamePasswordToken;
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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import com.goolecloud.domain.User;
import com.goolecloud.service.UserService;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userSerivce;
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行授权逻辑");
//1给资源授权
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//添加资源的授权字符串
//info.addStringPermission("user:add");
Subject subject = SecurityUtils.getSubject();
User user= (User) subject.getPrincipal();//获取下方传递过来的值//判断密码 SimpleAuthenticationInfo("",user.getPassword(),"");
User findById = userSerivce.findById(user.getId());
info.addStringPermission(findById.getPerms());
return info;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
System.out.println("执行认证逻辑");
String name="huangyi";
String password="123456";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken tPasswordToken=(UsernamePasswordToken) token;
if (!tPasswordToken.getUsername().equals(name)) {
//用户不存在
return null;
}
//判断密码
return new SimpleAuthenticationInfo("",user.password,"");
}
}
2.5整合MyBatis实现登录
2.5.1、导入mysql ,mybatis,依赖
2.5.2 、配置application.properties
#数据库连接配置
#驱动
spring.datasource.driverClassName = com.mysql.jdbc.Driver
#数据库链接
spring.datasource.url = jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false
#用户名
spring.datasource.username = root
#密码
spring.datasource.password = root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
logging.level.web=debug
logging.level.com.goolecloud=debug
server.port=8080
mybatis.type-aliases-package=com.goolecloud.domain
mybatis.mapper-locations=classpath:mapper/*.xml
2.5.3数据库
/*
Navicat Premium Data Transfer
Source Server : local
Source Server Type : MySQL
Source Server Version : 50728
Source Host : localhost:3306
Source Schema : user
Target Server Type : MySQL
Target Server Version : 50728
File Encoding : 65001
Date: 19/12/2019 22:04:44
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) NOT NULL,
`perms` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, 'admin', '123456', 'user:add');
INSERT INTO `user` VALUES (2, 'test', '123456', 'user:update');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
2.5.4 编写User实体
package com.goolecloud.domain;
public class User {
private Integer id;
private String name;
private String password;
private String perms;
public String getPerms() {
return perms;
}
public void setPerms(String perms) {
this.perms = perms;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", password=" + password + ", perms=" + perms + "]";
}
}
2.5.5编写Dao接口
package com.goolecloud.dao;
import com.goolecloud.domain.User;
public interface UserDao {
public User findByName(String name);
public User findById(Integer id);
}
2.5.6 编写UserMapper.xml映射文件
在resources目录下创建mapper文件夹,在创建UserMapper.xml
记住在 application.properties 中 mybatis.mapper-locations=classpath:mapper/*.xml 配置
<?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">
<!-- 该文件存放CRUD的sql语句 -->
<mapper namespace="com.goolecloud.dao.UserDao">
<select id="findByName" parameterType="string" resultType="user">
select * from user where name =#{value}
</select>
<select id="findById" parameterType="int" resultType="user">
select * from user where id=#{value}
</select>
</mapper>
2.5.7编写业务接口和实现
1.接口
package com.goolecloud.service;
import com.goolecloud.domain.User;
public interface UserService {
public User findByName(String name);
public User findById(Integer id);
}
2.实现
package com.goolecloud.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.goolecloud.dao.UserDao;
import com.goolecloud.domain.User;
import com.goolecloud.service.UserService;
@Service
public class UserServiceImpl implements UserService {
//注入Mapper接口
@Autowired
private UserDao dao;
@Override
public User findByName(String name) {
return dao.findByName(name);
}
@Override
public User findById(Integer id) {
// TODO Auto-generated method stub
return dao.findById(id);
}
}
其后在启动类上加入注解@MapperScan(basePackages = “com.goolecloud.dao”)
2.5.8修改UserRealm
package com.goolecloud.shiro;
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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import com.itheima.domain.User;
import com.itheima.service.UserService;
/**
* 自定义Realm
* @author
*
*/
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
return null;
}
@Autowired
private UserService userSerivce;
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
User user = userSerivce.findByName(token.getUsername());
if(user==null){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
}
2.6Spring Boot与Shiro整合实现用户授权
2.6.1使用Shiro内置过滤器拦截资源
package com.goolecloud.config;
import java.util.LinkedHashMap;
import java.util.Map;
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 com.goolecloud.shiro.UserRealm;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean shiFactoryBean(
@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean baBean = new ShiroFilterFactoryBean();
//设置安全管理器
baBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<String,String>();
// filterMap.put("/add", "authc");
// filterMap.put("/update", "authc");
filterMap.put("/login", "anon");
filterMap.put("/testThymeleaf", "anon");
// filterMap.put("/user/*", "authc");
//s授权过滤器 shiro会自动跳转到未授权页面
//修改部分一
filterMap.put("/add", "perms[user:add]");
filterMap.put("/update", "perms[user:update]");
filterMap.put("/*", "authc");
//修改调整的登录页面
baBean.setLoginUrl("/toLogin");
//修改部分二
baBean.setUnauthorizedUrl("/noAuth");
baBean.setFilterChainDefinitionMap(filterMap);
return baBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name="userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
2.6.2 完成Shiro的资源授权 并查询数据权限
UserRealm:
@Autowired
private UserService userSerivce;
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行授权逻辑");
//1给资源授权
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//添加资源的授权字符串
//info.addStringPermission("user:add");
Subject subject = SecurityUtils.getSubject();
User user= (User) subject.getPrincipal();//获取下方传递过来的值//判断密码 SimpleAuthenticationInfo("",user.getPassword(),"");
User findById = userSerivce.findById(user.getId());
info.addStringPermission(findById.getPerms());
return info;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
System.out.println("执行认证逻辑");
// String name="huangyi";
// String password="123456";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken tPasswordToken=(UsernamePasswordToken) token;
User user=userSerivce.findByName(tPasswordToken.getUsername());
if (user==null) {
return null;
}
// if (!tPasswordToken.getUsername().equals(name)) {
// //用户不存在
// return null;
// }
//判断密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
2.7 thymeleaf和shiro标签整合使用
2.7.1添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.7.2配置ShiroDialect
在ShiroConfig类里面添加getShiroDialect方法
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
2.7.3在页面上使用shiro标签
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试Thymeleaf的使用</title>
</head>
<body>
<h3 th:text="${name}"></h3>
<hr/>
<div shiro:hasPermission="user:add">
进入用户添加功能: <a href="add">用户添加</a><br/>
</div>
<div shiro:hasPermission="user:update">
进入用户更新功能: <a href="update">用户更新</a><br/>
</div>
<a href="toLogin">登录</a>
</body>
</html>
2.7.4 测试
admin 在数据库权限为 user:add 权限
只显示添加
我们在登录test账号
只有更新的权限