咱们先来普及一下什么是shiro,shiro原名Apache Shiro 是一个Java 的安全(权限)框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等高级应用。下载地址:http://shiro.apac he.org/
如图看shiro的功能和架构图:
话不多说,Springboot整合shiro,咱们直接上代码
pom.xml文件
<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.6.11</version> | |
<relativePath/> <!-- lookup parent from repository --> | |
</parent> | |
<groupId>com.example</groupId> | |
<artifactId>demo02</artifactId> | |
<version>0.0.1-SNAPSHOT</version> | |
<name>demo02</name> | |
<description>demo02</description> | |
<properties> | |
<java.version>1.8</java.version> | |
</properties> | |
<dependencies> | |
<dependency> | |
<groupId>org.apache.shiro</groupId> | |
<artifactId>shiro-spring</artifactId> | |
<version>1.4.1</version> | |
</dependency> | |
<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> | |
<version>8.0.31</version> | |
</dependency> | |
<!-- https://mvnrepository.com/artifact/log4j/log4j --> | |
<dependency> | |
<groupId>log4j</groupId> | |
<artifactId>log4j</artifactId> | |
<version>1.2.17</version> | |
</dependency> | |
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> | |
<dependency> | |
<groupId>com.alibaba</groupId> | |
<artifactId>druid</artifactId> | |
<version>1.1.12</version> | |
</dependency> | |
<dependency> | |
<groupId>org.projectlombok</groupId> | |
<artifactId>lombok</artifactId> | |
<version>1.18.18</version> | |
<scope>compile</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-thymeleaf</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.thymeleaf</groupId> | |
<artifactId>thymeleaf</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.thymeleaf.extras</groupId> | |
<artifactId>thymeleaf-extras-java8time</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-web</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-test</artifactId> | |
<scope>test</scope> | |
</dependency> | |
<dependency> | |
<groupId>com.github.theborakompanioni</groupId> | |
<artifactId>thymeleaf-extras-shiro</artifactId> | |
<version>2.0.0</version> | |
</dependency> | |
</dependencies> | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-compiler-plugin</artifactId> | |
<version>3.8.1</version> | |
<configuration> | |
<source>1.8</source> | |
<target>1.8</target> | |
<encoding>UTF-8</encoding> | |
</configuration> | |
</plugin> | |
<plugin> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-maven-plugin</artifactId> | |
</plugin> | |
</plugins> | |
</build> | |
</project> | |
然后我们建立一个数据库
/*
Navicat MySQL Data Transfer
Source Server :
Source Server Version : 80030
Source Host : localhost:3306
Source Database : mybatis
Target Server Type : MYSQL
Target Server Version : 80030
File Encoding : 65001
Date: 2023-03-14 18:00:05
*/
SET FOREIGN_KEY_CHECKS=0;
– Table structure for user
DROP TABLE IF EXISTS user
;
CREATE TABLE user
(
id
int NOT NULL AUTO_INCREMENT,
name
varchar(255) DEFAULT NULL,
pwd
varchar(255) DEFAULT NULL,
perms
varchar(100) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
– Records of user
INSERT INTO user
VALUES (‘1’, ‘qin’, ‘d1b129656359e35e95ebd56a63d7b9e0’, ‘user:add’);
INSERT INTO user
VALUES (‘2’, ‘hai’, ‘123’, ‘user:insert’);
INSERT INTO user
VALUES (‘3’, ‘root’, ‘d1b129656359e35e95ebd56a63d7b9e0’, ‘user:update’);
application.yml文件
spring: | |
datasource: | |
username: xxxx | |
password: xxxxxxxxxxxx | |
url: jdbc:mysql://localhost:3306/mybatis | |
driver-class-name: com.mysql.cj.jdbc.Driver | |
type: com.alibaba.druid.pool.DruidDataSource | |
mybatis: | |
mapper-locations: classpath:mapper/*tat.slowSqlMillis=500 |
controller层MyController类
package com.example.demo02.controller; | |
import lombok.extern.slf4j.Slf4j; | |
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; | |
import org.springframework.web.bind.annotation.RequestParam; | |
import org.springframework.web.bind.annotation.ResponseBody; | |
public class MyController { | |
"/") | (|
public String toIndex(Model model){ | |
model.addAttribute("msg","hello,shiro"); | |
return "login"; | |
} | |
"/user/add") | (|
public String add(){ | |
return "user/add"; | |
} | |
"/user/update") | (|
public String update(){ | |
return "user/update"; | |
} | |
"/toLogin") | (|
public String toLogin(){ | |
return "login"; | |
} | |
"/noauth") | (|
public String noAuth(){ | |
return "未经授权不能访问此页面"; | |
} | |
//登录操作 | |
"/login") | (|
public String login(String username, String password, (defaultValue = "false")boolean rememberMe,Model model){ | |
//使用shiro,编写认证操作 | |
//1. 获取Subject | |
Subject subject = SecurityUtils.getSubject(); | |
//2. 封装用户的数据 | |
UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe); | |
//3. 执行登录的方法,只要没有异常就代表登录成功! | |
try { | |
subject.login(token); //登录成功!返回首页 | |
System.out.println("输出认证成功跳转页面"); | |
return "index"; | |
} catch (UnknownAccountException e) { //用户名不存在 | |
model.addAttribute("msg","用户名不存在"); | |
return "login"; | |
} catch (IncorrectCredentialsException e) { //密码错误 | |
model.addAttribute("msg","密码错误"); | |
return "login"; | |
} | |
} | |
} | |
pojo层User
package com.example.demo02.pojo; | |
import lombok.AllArgsConstructor; | |
import lombok.Data; | |
import lombok.NoArgsConstructor; | |
public class User { | |
private int id; | |
private String name; | |
private String pwd; | |
private String perms; | |
} | |
config层配置两个类
第一个类ShiroConfig
package com.example.demo02.config; | |
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; | |
import org.apache.shiro.authc.credential.HashedCredentialsMatcher; | |
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; | |
//声明为配置类 | |
public class ShiroConfig { | |
//创建 ShiroFilterFactoryBean | |
public ShiroFilterFactoryBean | |
getShiroFilterFactoryBean("securityManager")DefaultWebSecurityManager securityManager){ ( | |
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); | |
//设置安全管理器 | |
shiroFilterFactoryBean.setSecurityManager(securityManager); | |
/* | |
添加Shiro内置过滤器,常用的有如下过滤器: | |
anon: 无需认证就可以访问 | |
authc: 必须认证才可以访问 | |
user: 如果使用了记住我功能就可以直接访问 | |
perms: 拥有某个资源权限才可以访问 | |
role: 拥有某个角色权限才可以访问 | |
* | |
/ | |
*/ | |
//进行一个拦截 | |
Map<String,String> filterMap = new LinkedHashMap<String, String>(); | |
// filterMap.put("/user/add","authc"); | |
// filterMap.put("/user/update","authc"); | |
//授权 | |
// filterMap.put("/user/add","perms[user:add]"); //大家记得注意顺序! | |
filterMap.put("/user/add","perms[user:add]"); | |
filterMap.put("/user/update","perms[user:update]"); | |
filterMap.put("/user/*","authc"); | |
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); | |
shiroFilterFactoryBean.setLoginUrl("/toLogin"); | |
//未授权页面 | |
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth"); | |
return shiroFilterFactoryBean; | |
} | |
//创建 DefaultWebSecurityManager | |
"securityManager") | (name =|
public DefaultWebSecurityManager | |
getDefaultWebSecurityManager("userRealm")UserRealm userRealm){ ( | |
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); | |
//2创建加密对象,设置相关属性 | |
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); | |
//2.1采用md5加密 | |
matcher.setHashAlgorithmName("md5"); | |
//2.2迭代加密次数 | |
matcher.setHashIterations(3); | |
//3将加密对象存储到myRealm中 | |
userRealm.setCredentialsMatcher(matcher); | |
//关联Realm | |
securityManager.setRealm(userRealm); | |
return securityManager; | |
} | |
//创建 realm 对象 | |
public UserRealm userRealm(){ | |
return new UserRealm(); | |
} | |
//配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用 | |
public ShiroDialect getShiroDialect(){ | |
return new ShiroDialect(); | |
} | |
} |
UserRealm
package com.example.demo02.config; | |
import com.example.demo02.pojo.User; | |
import com.example.demo02.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.subject.PrincipalCollection; | |
import org.apache.shiro.subject.Subject; | |
import org.apache.shiro.util.ByteSource; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import java.util.List; | |
//自定义得UserRaelm | |
public class UserRealm extends AuthorizingRealm { | |
UserService userService; | |
//授权 | |
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { | |
System.out.println("执行了=》授权doGetAuthorizationInfo"); | |
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); | |
// info.addStringPermission("user:update"); | |
info.addStringPermission("user:add"); | |
//拿到当前用户登陆对象 | |
Subject subject= SecurityUtils.getSubject(); | |
User currentUser= (User) subject.getPrincipal();//拿到User对象 | |
info.addStringPermission(currentUser.getPerms());//设置当前用户对象 | |
return info; | |
} | |
//执行认证逻辑 | |
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { | |
System.out.println("执行了=>认证逻辑AuthenticationToken"); | |
//假设数据库的用户名和密码 | |
// String name = "root"; | |
// String password = "123456"; | |
//1.判断用户名 | |
UsernamePasswordToken userToken = (UsernamePasswordToken)token; | |
//连接真实的数据库 | |
User user= userService.queryUserByName(userToken.getUsername()); | |
// | |
if(user==null){ | |
return null; | |
} | |
Subject subject = SecurityUtils.getSubject(); | |
subject.getSession().setAttribute("loginUser",user); | |
//2. 验证密码,我们可以使用一个AuthenticationInfo实现类SimpleAuthenticationInfo | |
// shiro会自动帮我们验证!重点是第二个参数就是要验证的密码! | |
return new SimpleAuthenticationInfo(user, user.getPwd(),ByteSource.Util.bytes("salt"),""); | |
// if(user !=null){ | |
// AuthenticationInfo info = new SimpleAuthenticationInfo( | |
// token.getPrincipal(), | |
// user.getPwd(), | |
// ByteSource.Util.bytes("salt"), | |
// token.getPrincipal().toString() | |
// ); | |
// return info; | |
// } | |
// return null; | |
} | |
} | |
service层
先是类UserServiceImpl
package com.example.demo02.service; | |
import com.example.demo02.mapper.UserMapper; | |
import com.example.demo02.pojo.User; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.stereotype.Service; | |
public class UserServiceImpl implements UserService { | |
UserMapper userMapper; | |
public User queryUserByName(String name) { | |
return userMapper.queryUserByName(name); | |
} | |
} |
再是接口UserService
package com.example.demo02.service; | |
import com.example.demo02.pojo.User; | |
public interface UserService { | |
public User queryUserByName(String name); | |
} |
mapper层
接口Usermapper
package com.example.demo02.mapper; | |
import com.example.demo02.pojo.User; | |
import org.apache.ibatis.annotations.Mapper; | |
import org.apache.ibatis.annotations.Param; | |
import org.apache.ibatis.annotations.Select; | |
import org.springframework.stereotype.Repository; | |
//@Mapper | |
public interface UserMapper { | |
public User queryUserByName( String name); | |
} | |
再就是前端resources里面的static和templates,由于文件过多不变多写,如果小伙伴们想要源码可以直接私聊我博客账号。
最后成功的成品如图:
普通用户登录:
root用户登录
后端实现鉴权图
存入数据库的数据为加密文件