文章目录
Shiro简介
shiro是一个简单的java安全框架,和SpringSecurity类似。Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。
shrio的三大核心对象:
-
subject
代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是subject
-
SecurityManager
安全管理器,即所有与安全有关的操作都会与SercurityManager交互,并且它管理着所有的Subject,它负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色 -
Realm
Shiro从Realm获取安全数据(如用户,角色,权限),连接数据。相当于DataSource。
三者之间的关系:
Shiro快速应用
springBoot整合shiro
一、准备工作
1、创建一个普通的SpringBoot项目,勾选web模块
2、导入thymeleaf的maven依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
创建一个普通的SpringBoot项目,导入Shiro的maven依赖
3、测试项目运行
编写简单的index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<span th:text="${msg}">
</span>
</body>
</html>
编写Controller类
@Controller
public class MyController {
@RequestMapping("/index")
public String index(Model model){
model.addAttribute("msg","hello,Shiro");
return "index";
}
}
测试运行访问index页面,成功,项目正常运行
二、整合SpringBoot环境搭建
1、导入整合依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
2、编写shiro配置类
在项目目录下创建config包,创建配置类。
编写Realm配置类
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
编写Shiro配置类:
@Configuration
public class MyShiroConfig {
//ShiroFilterFactory
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//关联DefaultWebSecurityManager,设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联Realm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建Realm对象,需要自定义类
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
3、在Controller类中添加方法:
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/del")
public String del(){
return "user/del";
}
4、编写简单的前端页面
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加用户</title>
</head>
<body>
<h1>添加用户</h1>
</body>
</html>
del.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>删除用户</title>
</head>
<body>
<h1>删除用户</h1>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1 style="text-align: center">首页</h1>
<span th:text="${msg}"></span>
<a th:href="@{/user/add}">添加用户</a>
<a th:href="@{/user/del}">删除用户</a>
</body>
</html>
运行项目,能正常跳转,环境搭建成功。
三、实现登录拦截
1、编写简单的登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div style="text-align: center">
<h1>登录</h1>
<form">
<P>user:<input name="user" type="text"></P>
<p>password: <input type="text" name="password"></p>
<button type="submit">提交</button>
</form>
</div>
</body>
</html>
2、在controller中添加跳转方法
@RequestMapping("/toLogin")
public String login(){
return "login";
}
3、在Shiro中添加拦截设置
Map<String,String> map = new LinkedHashMap<>();
//添加需要设置过滤权限的页面
map.put("/user/add","authc");
map.put("/user/del","authc");
//map.put("/user/*","authc");
bean.setFilterChainDefinitionMap(map);//添加
//设置登录请求,如果没有权限,就会被重定向到登录页面
bean.setLoginUrl("/toLogin");
return bean;
Shiro的常用内置过滤器:
anon: 无需认证就可以访问
authc: 必须认证才可以访问
user: 如果使用了记住我功能就可以直接访问
perms: 拥有某个资源权限才可以访问
role: 拥有某个角色权限才可以访问
运行项目成功拦截,登录拦截功能添加成功。
四、实现用户认证
1、给登录表单添加提交链接
<div style="text-align: center">
<h1>登录</h1>
<span th:text="${msg}"></span>
<form th:action="@{/login}" method="post">
<P>user:<input name="user" type="text"></P>
<p>password: <input type="text" name="password"></p>
<button type="submit">提交</button>
</form>
</div>
2、在controller中添加验证方法
@RequestMapping("/login")
public String login(String user,String password,Model model){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(user,password);
try{
subject.login(token);//执行登录方法,如果没有异常就ok
return "index";//没有异常,到主页
}catch (UnknownAccountException e){//用户名不存在
model.addAttribute("msg","用户名不存在");
return "login";
}catch (IncorrectCredentialsException e){//密码错误
model.addAttribute("msg","密码错误");
return "login";
}
}
3、在Realm配置类中添加认证
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("进行了认证");
String user = "root";
String password = "666666";
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
if (!token.getUsername().equals(user)){
return null;//如果用户名不匹配,返回null抛出异常
}
return new SimpleAuthenticationInfo("",password,"");
}
运行项目,可以正常提交表单,密码和用户都验证成功后,会重定向到主页。这时通过认证的用户可以查看主页的其他页的信息。
五、shiro整合mybatis
1、在maven中添加依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
2、绑定数据资源,配置数据源
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#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.Priority
#则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#整合mybatis
mybatis:
type-aliases-package: com.xhj.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
3、编写pojo实体类
package com.xhj.pojo;
public class user {
private int id;
private String name;
private String pwd;
public user() {
}
public user(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "user{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
4、编写Mapper层
@Repository
@Mapper
public interface UserMapper {
user selUser(String name);
}
5、实现mapper接口
<?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">
<mapper namespace="com.xhj.mapper.UserMapper">
<select id="selUser" parameterType="String" resultType="user">
select * from mybatis.user where name = #{name}
</select>
</mapper>
6、编写service层
@Service
public class UserService{
@Autowired
UserMapper userMapper;
public user selUserService(String name){
user user = userMapper.selUser(name);
return user;
}
}
7、修改Reaml类
将原本自定义的用户名和密码换成数据库资源。通过调用service层的方法,查询前端传递的参数,如果查询结果为空,则表示没有授权,如果不为空,则通过。
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("进行了认证");
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//调用service层的查询方法,根据前端传递的token里面的用户名与数据库认证
user user = userService.selUserService(token.getUsername());
//认证用户名
if (user==null){
return null;//如果user为空,说明没有在数据库中查到这个人,抛出异常
}
return new SimpleAuthenticationInfo("",user.getPwd(),"");
}
运行项目,发现用数据库中的数据才可以登录成功,并且登录过后可以访问主页的页面。认证通过。表明整合mybatis成功。
六、Shiro请求授权实现
1、在Shiro配置类设置访问权限
//未授权不能访问,会重定向到未授权页面
map.put("/user/add","perms[user:add]");//用户必须带有user:add字符串的授权才能访问
map.put("/user/del","perms[user:del]");
2、在Shiro配置类设置没有权限访问过滤
//设置未授权请求过滤
bean.setUnauthorizedUrl("/unauthorized");
3、在controller中添加未授权页面
@RequestMapping("/unauthorized")
@ResponseBody
public String unauthorized(){
return "未经授权无法访问此页面";
}
此时,如果用户没有访问权限会被定向到没有访问权限提示的页面。
4、在Shiro配置类中编写授权
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("进行了授权");
//实现一个授予权限的类SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的对象,这里的对象是在认证的时候通过SimpleAuthenticationInfo("",user.getPwd(),"");存入的
Subject Subject = SecurityUtils.getSubject();
user currentUser = (user)Subject.getPrincipal();//拿到user对象,通过user对象可以设置权限
//设置当前用户的权限,这里的权限是通过查询数据库中得来的
//授予权限
info.addStringPermission(currentUser.getPerms());
return info;
}
运行项目,只有在数据库中拥有权限的才能够访问对于的页面。
七、Shiro整合thymeleaf
1、添加maven依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2、添加命名空间
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
3、在Shiro配置类中,添加一个Dialect
//配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
4、修改前端页面
<body>
<h1 style="text-align: center">首页</h1>
<span th:text="${msg}"></span>
<div shiro:notAuthenticated>
<a th:href="@{/toLogin}">登录</a>
</div>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">添加用户</a>
</div>
<div shiro:hasPermission="user:del">
<a th:href="@{/user/del}">删除用户</a>
</div>
<a th:href="@{/logout}" th>注销</a>
</body>
在对应的页面上添加类似 shiro:hasPermission=“user:del” 的权限,只对拥有这个权限的用户开放这个页面。添加 shiro:notAuthenticated 表示:没有授权才显示。