转载自:https://blog.csdn.net/qq_33010161/article/details/88918985
什么是Shiro?
shiro是一个开源的安全管理框架,可以完成认证、授权、加密、会话管理、缓存等功能。
Shrio有哪些核心API,各有什么用?
Subject:用户主体,是和应用代码直接进行交互的对象;
SecurityManager:安全管理器,所有与安全相关的操作都会与SecurityManager进行交互,是shiro的核心;
Realm:连接数据库的桥梁
Shiro具有哪些功能?
Authentication:认证登录,验证用户的合法性
Authorization :授权,授予谁具有访问某些资源的权限
Session Management :会话管理,用户登录后的用户信息通过Session Management进行管理,也就是说用户登陆后就是一次会话,在他退出之前,他的所有信息都在会话中
Cryptography:密码模块,提供了一些加密算法
Web Supportweb:支持,可以很容易的集成到web环境中
Caching: 缓存,用户登陆后,其用户信息、所拥有的角色权限不必每次都查,这样 可以提高效率
Concurrency: shiro支持多线程应用的并发验证
Tesing:提供测试支持
Run As:允许一个用户假装另一个用户(如果他们允许的话)的身份进行访问
Remember Me:记住我,一次登陆后,下次就不用在登陆了
上面都是一些理论,接下来才是实际操作
1.创建SpringBoot工程
这里用的sts,Eclipse默认是不集成Spring的,没有的话请自行百度安装Spring插件,该案例用的是SpringBoot2+sts4,整个案例完成后目录如下
2.修改pom.xml文件,添加依赖
<dependencies>
<!-- SpringBoot集成的web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.6.2</version>
</dependency>
<!-- shiro与springboot整合依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--开发阶段使用,修改工程文件时,服务会自动重启 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis以及Oracle驱动依赖包
这里我用的Oracle驱动包安装到本地的,没有安装会报错
-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.1.0.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<type>pom</type>
</dependency>
<!-- thymeleaf对shiro的扩展 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
3.entry(实体类对象)
package com.zfsh.entry;
public class ShiroUser {
private Integer u_id;
private String u_name;
private String u_password;
private String u_dept;
public Integer getU_id() {
return u_id;
}
public void setU_id(Integer u_id) {
this.u_id = u_id;
}
public String getU_name() {
return u_name;
}
public void setU_name(String u_name) {
this.u_name = u_name;
}
public String getU_password() {
return u_password;
}
public void setU_password(String u_password) {
this.u_password = u_password;
}
public String getU_dept() {
return u_dept;
}
public void setU_dept(String u_dept) {
this.u_dept = u_dept;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((u_dept == null) ? 0 : u_dept.hashCode());
result = prime * result + ((u_id == null) ? 0 : u_id.hashCode());
result = prime * result + ((u_name == null) ? 0 : u_name.hashCode());
result = prime * result + ((u_password == null) ? 0 : u_password.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ShiroUser other = (ShiroUser) obj;
if (u_dept == null) {
if (other.u_dept != null)
return false;
} else if (!u_dept.equals(other.u_dept))
return false;
if (u_id == null) {
if (other.u_id != null)
return false;
} else if (!u_id.equals(other.u_id))
return false;
if (u_name == null) {
if (other.u_name != null)
return false;
} else if (!u_name.equals(other.u_name))
return false;
if (u_password == null) {
if (other.u_password != null)
return false;
} else if (!u_password.equals(other.u_password))
return false;
return true;
}
@Override
public String toString() {
return "ShiroUser [u_id=" + u_id + ", u_name=" + u_name + ", u_password=" + u_password + ", u_dept=" + u_dept
+ "]";
}
public ShiroUser(Integer u_id, String u_name, String u_password, String u_dept) {
super();
this.u_id = u_id;
this.u_name = u_name;
this.u_password = u_password;
this.u_dept = u_dept;
}
public ShiroUser() {
super();
}
}
4.Dao层(查询用户接口)
package com.zfsh.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.zfsh.entry.ShiroUser;
@Mapper
public interface ShiroUserDao {
public ShiroUser findByName(@Param(value="u_name")String name);
}
5.添加ShiroUserMapper.xml映射
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.zfsh.dao.ShiroUserDao">
<select id="findByName" resultType="ShiroUser" parameterType="String">
select * from shirouser where u_name=#{u_name}
</select>
</mapper>
6.添加Service业务层
package com.zfsh.service;
import org.apache.ibatis.annotations.Param;
import com.zfsh.entry.ShiroUser;
public interface ShiroUserService {
public ShiroUser findByName(@Param(value="u_name")String name);
}
7.添加业务层实现
package com.zfsh.serviceimp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zfsh.dao.ShiroUserDao;
import com.zfsh.entry.ShiroUser;
import com.zfsh.service.ShiroUserService;
@Service
public class ShiroUserServiceImp implements ShiroUserService{
//引入dao接口方法
@Autowired
private ShiroUserDao shiroUserDao;
@Override
public ShiroUser findByName(String name) {
ShiroUser user = shiroUserDao.findByName(name);
return user;
}
}
8.添加Controller控制器
package com.zfsh.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 ActionController {
@RequestMapping("toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("toAdd")
public String toAdd() {
return "user/adduser";
}
@RequestMapping("toUpdate")
public String toUpdate() {
return "user/updateuser";
}
@RequestMapping("toIndex")
public String toIndex() {
return "index";
}
//未授权页面跳转
@RequestMapping("toUnAuth")
public String unAuth() {
return "unAyth";
}
//登录
@RequestMapping("login")
public String login(String name,String password,Model model) {
//编写Shiro认证操作
//获取Subject
Subject subject = SecurityUtils.getSubject();
//封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//执行登录方法
try {
subject.login(token);
//没有异常就登录成功
return "redirect:toIndex";
} catch (UnknownAccountException e) {
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
return "login";
}
}
}
9.编写Shiro类配置
package com.zfsh.shiro;
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 at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
/*
* shiro配置类
* */
@Configuration
public class ShiroConfig {
//创建ShiroFilterFactoryBean(用户主体,把操作交给SecurityManager)
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加shiro内置过滤器,可以实现相关权限拦截
/**
* anon:无需认证(登录)就可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能才可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色的授权才可以访问
* */
Map<String, String> filerMap = new LinkedHashMap<>();
// filerMap.put("/toAdd","authc"); //拦截指定页面
// filerMap.put("/toUpdate","authc");
filerMap.put("/toIndex", "anon"); //放行
filerMap.put("/login", "anon");
//授权过滤器
//Tips:当授权拦截后,Shiro会自动跳转到未授权界面
filerMap.put("/toAdd", "perms[user:add]");
filerMap.put("/toUpdate", "perms[user:update]");
//通配符拦截,拦截所有页面
filerMap.put("/*", "authc");
//修改跳转登陆的页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//设置未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/toUnAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filerMap);
return shiroFilterFactoryBean;
}
//创建DefaultWebSecurityManager(安全管理器,关联Realm)
@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();
}
//配置ShiroDialect,用于thymeleaf和shiro标签的配合使用
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
}
10.自定义Realm
package com.zfsh.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.zfsh.entry.ShiroUser;
import com.zfsh.service.ShiroUserService;
//自定义Realm
//继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm{
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行授权逻辑");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加授权字符串
//该处info.addStringPermission()中字符要与数据库权限字段保持一致
info.addStringPermission("user:add");
info.addStringPermission("user:update");
//获取当前用户的ID
Subject subject = SecurityUtils.getSubject();
ShiroUser user = (ShiroUser) subject.getPrincipal();
ShiroUser dbUser = shiroUserService.findByName(user.getU_name());
System.out.println(dbUser);
info.addStringPermission(dbUser.getU_dept());
return info;
}
@Autowired
private ShiroUserService shiroUserService;
//执行认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行认证逻辑");
//1.判断用户名
UsernamePasswordToken newToken = (UsernamePasswordToken)token;
ShiroUser user = shiroUserService.findByName(newToken.getUsername());
if(user==null) {
//用户名不存在
return null;//Shiro底层会抛出UnkownAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo(user,user.getU_password(),"");
}
}
11.修改application.properties配置文件
#修改默认端口号为8089
server.port=8089
spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5
#数据源配置
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.username=ht
spring.datasource.password=hting
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
#起别名
mybatis.type-aliases-package=com.zfsh.entry
#查找自定义目录下的mapper映射文件
mybatis.mapper-locations=classpath:com/zfsh/mapper/*Mapper.xml
12.后台代码以及全部写完,现在编写前台代码
12.1:在src/main/resources/templates下创建user文件目录存储关于用户的html网页
12.2:在templates根目录编写index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1 align="center" th:text="${msg}"></h1>
<!-- 判断是否有添加权限 -->
<div shiro:hasPermission="user:add">
进入用户添加:<a href="toAdd">用户添加</a><br />
</div>
<!-- 判断是否有更新权限 -->
<div shiro:hasPermission="user:update">
进入用户更新:<a href="toUpdate">用户更新</a><br />
</div>
<a href="toLogin">登录</a>
</body>
</html>
12.3:在templates根目录编写login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1 align="center">登录页面</h1>
<h3 style="color: red" th:text="${msg}"></h3>
<form action="login" method="post">
用户名:<input type="text" name="name"/><br />
密码:<input type="password" name="password"/><br />
<input type="submit" value="登录"/>
</form>
</body>
</html>
12.4:在templates根目录下编写unAyth.html未授权显示页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>未授权</title>
</head>
<body>
<h1 style="color: red" align="center">对不起,你暂无该权限</h1>
</body>
</html>
12.5:在user目录下编写简单的adduser.html(仅做显示测试用)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>添加用户</title>
</head>
<body>
<h1>用户添加</h1>
</body>
</html>
12.6:在user目录下编写简单的updateuser.html(仅做显示测试用)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>修改个人信息</title>
</head>
<body>
<h1>用户更新</h1>
</body>
</html>
数据库表的编写:
create table shirouser(
u_id integer, -- 用户ID
u_name varchar2(60), -- 用户姓名
u_password varchar2(100), -- 用户密码
u_dept varchar2(100) -- 用户的权限
);
insert into shirouser(u_id,u_name,u_password,u_dept) values(1,'sansan','aaa','开发部');
commit;
insert into shirouser(u_id,u_name,u_password,u_dept) values(2,'lisi','bbb','人事部');
commit;
全部代码编写完成,现在启动服务器测试
如何启动?
双击ShiroApplication.java这个类,右键Run As-->Spring Boot App即可
打开浏览器在地址栏输入:http://localhost:8089/toIndex,说明未登录拦截成功,进入登录页面。
其他就不测了,代码量有点大!!!