目录
1.创建项目
1.1、导入相关依赖
1.2、创建结构目录(如下图)
1.3、静态页面设置
1.4、Controller层
1.5、Config
2.Shiro实现相关功能
2.1、Shiro实现登录拦截
2.2、Shiro实现用户认证
2.3、Shiro整合Mybatis
2.4、Shiro请求授权实现
2.5、Shiro整合Thymeleaf
1.创建项目
1.1、导入相关依赖
thymeleaf模板
org.thymeleaf
thymeleaf-spring5
org.thymeleaf.extras
thymeleaf-extras-java8time
org.apache.shiro
shiro-spring
1.4.1
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
1.2、创建结构目录(如下图)
thymeleaf模板
org.thymeleaf
thymeleaf-spring5
org.thymeleaf.extras
thymeleaf-extras-java8time
org.apache.shiro shiro-spring1.4.1 org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-testtest org.junit.vintage junit-vintage-engine
1.2、创建结构目录(如下图)
1.3、静态页面设置
注意添加thymeleaf的域名空间:
xmlns:xmls="http://www.w3.org/1999/xhtml" xmls:th="http://www.thymeleaf.org"
index.html
Title
首页
添加
修改
删除
页面展示:(并不是很beautiful)
add.html
Title
添加
页面展示:
update.html
Title
修改
页面展示:
delete.html
Title
删除
页面展示:
1.4、Controller层
通过Controller来进行各个页面之间的跳转
package com.jun.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping({"/", "/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("user/delete")
public String delete(){
return "user/delete";
}
}
1.5、Config
Shiro三大对象:
原文链接:https://blog.csdn.net/qq_41430393/article/details/87730198
1.Subject(主体)
应用代码的直接交互对象就是Subject,也就是说Shiro对外的核心API就是Subject,Subject代表了当前“用户”,这个用户不是指具体的某一个人,可以说与当前应用交互的任何东西都是Subject,与Subject的所有交互都会委托给SecurityManager来执行,可以理解为Subject只是一个充当门面的,真正的幕后老大是SecurityManager,SecurityManager才是实际的执行者。
2.SecurityManager(安全管理器)
所有与安全有关的操作都会与SecurityManager进行交互,并且SecurityManager管理着所有的Subject,可以看出它才是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMvc中的dispatcherServlet(前端控制器)的角色。
3.Realm
Shiro从Realm获取安全数据(用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法,也需要从Realm获取用户的角色权限来判断用户是否能进行一系列操作。可以把Realm看作DataSource数据源.
首先文明需要自定义一个Raelm
该类需要继承 extends AuthorizingRealm 表明该类是一个自定义的Realm
重写两个方法:
doGetAuthorizationInfo //授权
doGetAuthenticationInfo //认证
具体代码如下:
package com.jun.config;
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 extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权==>doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证==>doGetAuthenticationInfo");
return null;
}
}
创建一个 ShiroConfig类,在此类中分别按顺序实现一下三种方法,并必须放到Bean中
1、创建 realm对象,需要自定义 ,调用之前创建的Realm
//创建 realm对象,需要自定义 1
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
2、DefaultWebSecurityManager 2
注意:在这个方法中
securityManager.setRealm(userRealm);
userRealm并不能够直接调用userRealm方法中的对象,我们要通过
(@Qualifier("userRealm") UserRealm userRealm)
@Qualifier这个注解来将对象作为实参,实现对象的调用,
//DefaultWebSecurityManager 2
@Bean(name="securityManager")
public DefaultWebSecurityManager dafaultWebSecurityMansger(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联Realm
securityManager.setRealm(userRealm);
return securityManager;
}
3、ShiroFilterFactoryBean 3
//ShiroFilterFactoryBean 3
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityMansger){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityMansger);
return bean;
}
附:
2.Shiro实现相关功能
2.1、Shiro实现登录拦截
Shiro的内置过滤器:
anon: 无须认证就可以访问
authc: 必须认证才能访问
user: 必须拥有 记住我 才能访问
perms: 必须拥有某个资源的权限才能访问
role: 拥有某个角色权限才能访问
- 在ShiroConfig的 ShiroFilterFactoryBean 方法中写入Shiro的内置过滤器
Map filterMap = new linkedHashMap<>();
// filterMap.put("/user/add","authc");
// filterMap.put("/user/update","authc");
// filterMap.put("/user/delete","authc");
filterMap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
- 创建一个登录界面login.html
Title
登录
Shiro的内置过滤器:
anon: 无须认证就可以访问 authc: 必须认证才能访问 user: 必须拥有 记住我 才能访问 perms: 必须拥有某个资源的权限才能访问 role: 拥有某个角色权限才能访问
- 在ShiroConfig的 ShiroFilterFactoryBean 方法中写入Shiro的内置过滤器
MapfilterMap = new linkedHashMap<>(); // filterMap.put("/user/add","authc"); // filterMap.put("/user/update","authc"); // filterMap.put("/user/delete","authc"); filterMap.put("/user/*","authc"); bean.setFilterChainDefinitionMap(filterMap);
- 创建一个登录界面login.html
Title
登录
- 在MyController这个类中添加登陆页面的Mapping
@RequestMapping("/toLogin") public String toLogin(){ return "login"; } - 将登录页面添加到Bean中
bean.setLoginUrl("/toLogin");
2.2、Shiro实现用户认证
- 在MyController中添加登录方法
@RequestMapping("/login")
public String login(String username ,String password,Model model) {
//通过Model给前端传递一个值
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//验证用户名和密码
try{
subject.login(token);//执行登录的方法,如果没有异常就说明OK了
return "index";
}catch (UnknownAccountException e){//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}catch(IncorrectCredentialsException e){//密码错误
model.addAttribute("msg","密码错误");
return "login";
}
- 前端接收后端传递的Model值
- 在UserRealm类中模拟数据,并实现认证
//用户名,密码 数据库中取
String username = "root";
String password = "123456";
//将authenticationToken 强转成我们所需要的 UsernamePasswordToken 类型
UsernamePasswordToken usertoken = (UsernamePasswordToken) authenticationToken;
//用户名认证
if(!usertoken.getUsername().equals(username)){
return null;//抛出一个异常 UnknownAccountException
}
//密码认证,Shiro做~
return new SimpleAuthenticationInfo("",password,"");
2.3、Shiro整合Mybatis
- 2.3.1、导入需要的依赖
org.projectlombok
lombok
1.16.10
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
com.alibaba
druid
1.1.12
log4j
log4j
1.2.17
mysql
mysql-connector-java
- 创建并编写yaml配置文件
spring:
datasource:
username: stu
password: 123456
#假如时区报错了,就在url中添加时区 serverTimezone=UTC
url: jdbc:mysql://(端口号)/stu?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
# 设置数据源为Druid
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
- 连接数据库
- 2.3.1、导入需要的依赖
org.projectlombok
lombok
1.16.10
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
com.alibaba
druid
1.1.12
log4j
log4j
1.2.17
mysql
mysql-connector-java
- 创建并编写yaml配置文件
spring:
datasource:
username: stu
password: 123456
#假如时区报错了,就在url中添加时区 serverTimezone=UTC
url: jdbc:mysql://(端口号)/stu?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
# 设置数据源为Druid
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
- 连接数据库
编写pojo实体类
package com.jun.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private int id;
private String name;
private String pwd;
}
- 编写Mapper
package com.jun.mapper;
import com.jun.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface UserMapper {
public User queryUserByName(String name);
}
配置Mapper的资源文件
select * from stu.student where name = #{name }
- 编写service层
接口:
package com.jun.service;
import com.jun.pojo.User;
public interface UserService {
public User queryUserByName(String name);
}
实现类:
package com.jun.service;
import com.jun.mapper.UserMapper;
import com.jun.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
- 将之前模拟的数据,改成真实的数据库数据(在自定义的Realm中)
// //用户名,密码 数据库中取
// String username = "root";
// String password = "123456";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//连接真实的数据库
User user = userService.queryUserByName(userToken.getUsername());
if(user == null){//如果没有这个人
return null;//UnknownAccountException
}
//密码认证,Shiro做~
return new SimpleAuthenticationInfo("",user.getPwd(),"");
2.4、Shiro请求授权实现
- 在数据库中定义用户的权限(perms)
- 设置未授权页面
直接通过Controller实现
@GetMapping("/unauth")
@ResponseBody
public String unauthorized(){
return "未经授权无法访问此页面";
}
- 定义需要授权的页面
//授权,正常情况是没有授权会跳到未授权页面
filterMap.put("/user/add" , "perms[user:add]");
filterMap.put("/user/update" , "perms[user:update]");
filterMap.put("/user/delete" , "perms[user:delete]");
为授权就跳转到未授权页面
//未授权页面
bean.setUnauthorizedUrl("/unauth");
- Realm授权
//SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addStringPermission("user:add");
// info.addStringPermission("user:update");
// info.addStringPermission("user:delete");
//拿到当前登陆的对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到User对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
2.5、Shiro整合Thymeleaf
- 导入相关依赖
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
- 整合ShiroDialect
在ShiroController中
//整合ShiroDialect:用来整个 Shiro 和Thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
- 设置session
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
前端首页:
Title
首页
登录
添加
修改
删除



