- Shiro 框架简介
- SpringBoot与Shiro框架整合实现用户认证
- SpringBoot与Shiro框架整合实现用户授权
- thymeleaf与Shiro标签整合实现
1. shiro框架简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序
Apache Shiro 体系结构
• Authentication 认证 ---- 用户登录
• Authorization 授权 — 用户具有哪些权限
• Cryptography 安全数据加密
• Session Management 会话管理
• Web Integration web系统集成
• Interations 集成其它应用,spring、缓存框架
2. 工程搭建
1.新建springboot工程。
pom.xml文件如下
<?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 http://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>1.5.21.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>cn.zshuai</groupId>
<artifactId>Springboot_shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Springboot_shiro</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- web支持,SpringMVC, Servlet支持等 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
可以简单编写测试类,测试是否可以运行,或者直接运行启动类看输出。
2.导入thymeleaf页面模块
引入thymeleaf依赖
<!-- 导入thymeleaf依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
编写controller测试方法
/**
* 测试thymeieaf
*/
@RequestMapping("/thymeleaf")
public String testThymeieaf(Model model) {
//把数据存入model
model.addAttribute("name","zshuai");
//返回到指定的页面(test.html)
return "test";
}
建立test.html页面
在src/main/resource目录下创建templates目录,然后创建test.html页面,内容随便写
启动起来项目访问http://localhost:8080/thymeleaf。会出错,跳转不过去
在thymeleaf3.0以前对页面标签语法要求比较严格,开始标签必须有对应的结束标签。
如果希望页面语法不严谨,但是也能够运行成功,可以把thymeleaf升级为3.0或以上版本。
升级thymeleaf3.0.2版本:
<!-- 修改参数 -->
<properties>
<java.version>1.8</java.version>
<!-- 修改thymeleaf的版本 -->
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
</properties>
项目初步搭建成功。
3.SpringBoot与Shiro框架整合实现用户认证
1.Shiro核心API
Subject: 用户主体(把操作交给SecurityManager)
SecurityManager:安全管理器(关联Realm)
Realm:Shiro连接数据的桥梁
Spring Boot整合Shiro
2.导入shiro与spring整合依赖
修改pom.xml
<!-- shiro与spring整合依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
3.自定义Realm类
package cn.zshuai.shiro;
import java.util.List;
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 cn.zshuai.entity.SysUser;
import cn.zshuai.service.UserService;
public class UserRealm extends AuthorizingRealm {
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// TODO Auto-generated method stub
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Autowired
private UserService userSerivce;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
System.out.println("执行认证逻辑");
return null;
}
4.编写Shiro配置类
package cn.zshuai.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;
@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();
}
}
5.使用Shiro内置过滤器实现页面拦截
package cn.zshuai.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;
@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("/thymeleaf", "anon");//不拦截
//放行login.html页面
filterMap.put("/login", "anon");
filterMap.put("/*", "authc");//拦截所有请求
//两种拦截形式
shiroFilterFactoryBean.setLoginUrl("/toLogin");//被拦截后的跳转路径
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
.......................................
}
用户进行操作时候跳转的对应路径都会根据上面配置的规则进行拦截与放行。现在是访问页面会被拦截并跳转到登录页面。编写登录页面,进行下一步的操作
6.登录页面准备
login.html
<!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>
起对应的controller方法
/**
* 登录逻辑处理
*/
@RequestMapping("/login")
public String login(String name,String password,Model model){
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
//跳转到test.html
return "redirect:/thymeleaf";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败:密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
7.在realm中进行用户信息的判断
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//这里应该进行数据库信息的查询与比较,所以先集成数据库
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
if(!token.getUsername().equals(name)){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo("",password,"");
}
}
需要查询数据库信息,先集成数据库
- 整合mybaties
导入mybatis相关的依赖
<!-- 导入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>
</dependency>
<!-- SpringBoot的Mybatis启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
- 配置application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/SSM-Crm?useSSL=false
spring.datasource.username=root
spring.datasource.password=rootzs
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.type-aliases-package=cn.zshuai.entity //设置的数据库对应的实体别名
- 使用mybaties逆向工程完成实体类、mapper等文件的生成,然后放入对应的路径下
项目结构如下
- 创建对应的serivce和实现类
参照上方截图位置创建
service
package cn.zshuai.service;
import java.util.List;
import cn.zshuai.entity.SysUser;
public interface UserService {
public List<SysUser> findByName(String name);
}
serviceimpl
package cn.zshuai.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.zshuai.entity.SysUser;
import cn.zshuai.entity.SysUserExample;
import cn.zshuai.entity.SysUserExample.Criteria;
import cn.zshuai.mapper.SysUserMapper;
import cn.zshuai.service.UserService;
@Service
public class UserServiceImpl implements UserService {
//注入mapper
@Autowired
private SysUserMapper userMapper;
@Override
public List<SysUser> findByName(String name) {
SysUserExample example = new SysUserExample();
Criteria criteria = example.createCriteria();
criteria.andUserNameEqualTo(name);
List<SysUser> userList = userMapper.selectByExample(example);
return userList;
}
}
- 在启动类中添加mapper扫描,不然会加载不到mapper,注意位置不要出错
package cn.zshuai;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("cn.zshuai.mapper")
public class SpringbootShiroApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootShiroApplication.class, args);
}
}
- 修改userRealm
/**
* 执行认证逻辑
*/
@Autowired
private UserService userSerivce;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
System.out.println("执行认证逻辑");
// 1. 编写shiro判断逻辑,判断用户名和密码
UsernamePasswordToken pojo = (UsernamePasswordToken) token;
List<SysUser> userList = userSerivce.findByName(pojo.getUsername());
if (userList.size() == 0) {
// 用户名字不存在
return null;// shiro底层会抛出UnKnowAccountException
}
// 2. 判断密码 空参即可
return new SimpleAuthenticationInfo(userList.get(0), userList.get(0).getUserPassword(), "");
}
到这里就可以实现拦截之后跳转登录,并与数据库中user信息对比后实现登录功能一系列的操作。
4.SpringBoot与Shiro框架整合实现用户授权
1.使用Shiro内置过滤器拦截资源
Map<String,String> filterMap = new LinkedHashMap<String,String>();
//拦截的都是请求路径,并不是直接拦截资源位置
/*filterMap.put("/add", "authc");
filterMap.put("/update", "authc");*/
filterMap.put("/thymeleaf", "anon");//不拦截
//放行login.html页面
filterMap.put("/login", "anon");
/**
* 授权过滤器
*/
//注意: 当前授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/add", "perms[add]");
filterMap.put("/update", "perms[update]");
filterMap.put("/*", "authc");//拦截所有请求
注意顺序,一定要在拦截所有请求之前。
2.完成Shiro的资源授权
UserRealm:
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加资源的授权字符串
info.addStringPermission("add");
return info;
}
当然,也可以通过查询数据库信息,对其进行授权。
表结构如下
通过user_code来看其有什么权限,然后赋予,如果是admin,就赋予全部的权限
package cn.zshuai.shiro;
import java.util.List;
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 cn.zshuai.entity.SysUser;
import cn.zshuai.service.UserService;
public class UserRealm extends AuthorizingRealm {
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// TODO Auto-generated method stub
System.out.println("执行授权逻辑");
// 给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加资源的授权字符串
//info.addStringPermission("add");
//到数据库查询当前登录用户的授权字符串
//获取当前登录用户
Subject subject = SecurityUtils.getSubject();
//注意:这里的user实体是通过下面的return new SimpleAuthenticationInfo(userList.get(0), userList.get(0).getUserPassword(), "");方法的第一个参数传递过来的
SysUser user = (SysUser)subject.getPrincipal();
if (user.getUserName().equals("admin")) {
//管理员,赋予全部权限
info.addStringPermission("add");
info.addStringPermission("update");
}else {
SysUser dbUser = userSerivce.findById(user.getUserId());
info.addStringPermission(dbUser.getUserCode());
}
return info;
}
/**
* 执行认证逻辑
*/
@Autowired
private UserService userSerivce;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
System.out.println("执行认证逻辑");
// 1. 编写shiro判断逻辑,判断用户名和密码
UsernamePasswordToken pojo = (UsernamePasswordToken) token;
List<SysUser> userList = userSerivce.findByName(pojo.getUsername());
if (userList.size() == 0) {
// 用户名字不存在
return null;// shiro底层会抛出UnKnowAccountException
}
// 2. 判断密码 空参即可
return new SimpleAuthenticationInfo(userList.get(0), userList.get(0).getUserPassword(), "");
}
}
5 thymeleaf和shiro标签整合使用
1.导入thymeleaf扩展坐标
<!-- thymel对shiro的扩展坐标 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2.配置ShiroDialect
在ShiroConfig类里面添加getShiroDialect方法
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
3.在页面上使用shiro标签
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试Thymeleaf的使用</title>
</head>
<body>
<h3 th:text="${name}"></h3>
<div shiro:hasPermission="add">
进入用户添加功能: <a href="add">用户添加</a><br />
</div>
<div shiro:hasPermission="update">
进入用户更新功能: <a href="update">用户更新</a><br />
</div>
<a href="toLogin">登录</a>
</body>
</html>
这样就可以做到,根据不同用户的不同权限动态在页面上动态显示其权限下能够显得操作。