SpringBoot与Shiro整合
•
数据库
一,Shiro 体系结构
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

- Authentication 认证 —- 用户登录
- Authorization 授权 —- 用户具有哪些权限
- Cryptography 安全数据加密
- Session Management 会话管理
- Web Integration web系统集成
- Interations 集成其它应用,spring、缓存框架
二,构建spring boot工程
建立Maven项目


修改pom.xml
- 继承Spring Boot 父工程
org.springframework.boot
spring-boot-starter-parent
2.5.3
- 添加web支持
org.springframework.boot
spring-boot-starter-web
编写spring Boot启动类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Description: SpringBoot启动类
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
编写测试Controller类
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Description:控制层
*/
@Controller
public class TestController {
/**
* 测试方法
*/
@GetMapping("/hello")
@ResponseBody
public String hello(){
System.out.println("UserController.hello()");
return "ok";
}
}
启动,测试

三,引入thymeleaf页面模块
修改pom.xml
- 添加thymeleaf依赖
org.springframework.boot
spring-boot-starter-thymeleaf
在Controller添加测试方法
/**
* 测试thymeleaf
*/
@RequestMapping("/test")
public String testThymeleaf(Model model){
//把数据存入model
model.addAttribute("name", "张三");
//返回test.html
return "test";
}
建立test.html页面
- 在src/main/resource目录下创建templates目录,然后创建test.html页面
Title
启动,测试

四, Spring Boot与Shiro整合实现用户认证
核心API
- Subject: 用户主体(把操作交给SecurityManager)
- SecurityManager:安全管理器(关联Realm)
- Realm:Shiro连接数据的桥梁
修改pom.xml
- 添加shiro与spring整合依赖
org.apache.shiro
shiro-spring
1.10.1
创建Realm类
package com.example.shiro;
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;
/**
* @Description:自定义Realm 处理登录 权限
*/
public class UserRleam extends AuthorizingRealm {
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
return null;
}
}
编写Shiro配置类
package com.example.shiro;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Description:shiro配置类
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
Map filterMap = new LinkedHashMap();
filterMap.put("/hello", "anon");
filterMap.put("/login", "anon");
filterMap.put("/**", "authc");
//要求登陆时的链接,非必须。
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean
public UserRealm getRealm(){
return new UserRealm();
}
}
创建登录页面
- 在src/main/resource目录下创建templates目录,然后创建login.html页面
登陆页面
登录
用户名:
密码:
编写Controller的登录逻辑
- 在TestController.java类中添加方法
/**
* 登陆页面跳转
*/
@GetMapping("/login")
public String login(){
return "login";
}
/**
* 登录逻辑处理
*/
@PostMapping("/login")
public String login(String username,String password,Model model){
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
//跳转到test.html
return "redirect:/test";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登录失败:用户名不存在
System.out.println("用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败:密码错误
System.out.println("密码错误");
return "login";
}
}
编写Realm的认证逻辑判断
package com.example.shiro;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @Description:自定义Realm 处理登录 权限
*/
public class UserRealm extends AuthorizingRealm {
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库的用户名和密码
String username = "aaa";
String password = "123";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
if(!token.getUsername().equals(username)){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
/**
* 对比密码
* 参数1:主体对象,按需要传,登陆成功后该参数可通过SecurityUtils.getSubject().getPrincipal()获取。
* 参数2:从对象中取密码,users.getPassword()是这个用户的数据库中的密码 是用来和authenticationToken里的密码比对
* 参数3:盐,可以为空
* 参数4:当前realm的名字
*/
return new SimpleAuthenticationInfo("flk好帅", password,null, getName());
}
}
启动,测试
- 先访问localhost:8080/hello

由于在shiro过滤器中添加了filterMap.put(“/hello”, “anon”);,所以无需认证(登录)就可以访问/hello
- 访问localhost:8080/test

由于在shiro过滤器中添加了filterMap.put(“/**”, “authc”);,所以必须认证才可以访问/test,页面便跳转到登陆页面/login
- 输入正确的账号和密码

可以正常进入到test.html。
- 输入错误的账号或密码

控制台

账号或密码不正常,根据controller层的判断逻辑会跳转到登陆页面/login
五,Spring Boot整合MyBatis实现登录
导入mybatis相关的依赖
com.alibaba druid-spring-boot-starter 1.2.15 mysql mysql-connector-java org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.3
配置application.yml
在src/main/resources目录下创建application.yml文件,并添加配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: admin
type: com.alibaba.druid.pool.DruidDataSource
# MyBatis
mybatis:
# 搜索指定包别名
type-aliases-package: com.example.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 日志配置
logging:
level:
com.example: debug
创建数据库,并创建表
- 表结构:

- 再插入一条数据:

添加User.java
package com.example.domain;/** * @Description:User类 */public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; }}添加UserMapper.java
package com.example.mapper;import com.example.domain.User;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;/** * @Description:Usermapper */public interface UserMapper { User findByUsername(@Param("username") String username);}添加UserMapper.xml
在src/main/resources/mapper目录下
SELECT id,
username,
password
FROM
user where username = #{username}
添加Service层
- 接口
package com.example.service;
import com.example.domain.User;
/**
* @Description:IUserService
*/
public interface IUserService {
/**
* 根据用户名查询用户
* @param username 用户名
* @return
*/
User findByUsername (String username);
}
- 实现
package com.example.service.impl;
import com.example.domain.User;
import com.example.mapper.UserMapper;
import com.example.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Description:UserServiceImpl
*/
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public User findByUsername(String username) {
return userMapper.findByUsername(username);
}
}
在启动类Application.java中添加@MapperScan注解
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Description: SpringBoot启动类
*/
@SpringBootApplication
//@MapperScan指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
@MapperScan("com.example.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
修改UserRealm中doGetAuthenticationInfo方法的认证逻辑
package com.example.shiro;
import com.example.domain.User;
import com.example.service.IUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @Description:自定义Realm 处理登录 权限
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
private IUserService userService;
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库的用户名和密码
// String username = "aaa";
// String password = "123";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//从数据库中获取用户信息
User user = userService.findByUsername(token.getUsername());
if(user == null){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
/**
* 对比密码
* 参数1:主体对象,按需要传,登陆成功后该参数可通过SecurityUtils.getSubject().getPrincipal()获取。
* 参数2:从对象中取密码,users.getPassword()是这个用户的数据库中的密码 是用来和authenticationToken里的密码比对
* 参数3:盐,可以为空
* 参数4:当前realm的名字
*/
return new SimpleAuthenticationInfo("flk好帅", user.getPassword(),null, getName());
}
}
启动,测试
- 输入账号密码

- 控制台成功打印日志

六,Spring Boot与Shiro整合实现用户授权
修改pom.xml
- 添加aop依赖,方便开启shiro注解
org.springframework.boot
spring-boot-starter-aop
修改ShiroConfig.java
- 开启Shiro注解
/**
* 开启Shiro注解
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
DefaultWebSecurityManager defaultWebSecurityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
return authorizationAttributeSourceAdvisor;
}
完善UserRealm的doGetAuthorizationInfo授权逻辑
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//假设数据库的aaa用户的权限只有user:add
info.addStringPermission("user:add");
//info.addStringPermission("user:update");
return info;
}
添加两个页面
- 在src/main/resource目录下创建templates目录,然后创建add.html页面
add
addPage
- 在src/main/resource目录下创建templates目录,然后创建update.html页面
update
updatePage
修改test.html页面
- 添加add.html和update.html的跳转链接
Title
进入添加页面: 添加
进入更新功能: 更新
修改TestController.java添加页面跳转方法
/**
* 添加页面跳转
*/
//需要user:add权限才能访问
@RequiresPermissions("user:add")
@GetMapping("/add")
public String add(){
return "add";
}
/**
* 添加页面跳转
*/
//需要user:update权限才能访问
@RequiresPermissions("user:update")
@GetMapping("/update")
public String update(){
return "update";
}
启动,测试
- 登陆成功后,进入add添加页面

成功进入,因为它有user:add这个权限。
– 控制台打印:

- 登陆成功后,进入update更新页面

失败进入,因为它没有user:update这个权限。
– 控制台打印:

(后续可以做全局异常捕获跳转到提示页面,我这边没有去弄。。。)
七,thymeleaf和shiro标签整合使用
修改pom.xml
添加thymeleaf与shiro的扩展
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
修改ShiroConfig.java
配置ShiroDialect,用于thymeleaf和shiro标签配合使用
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
修改test.html页面
Title
进入添加页面: 添加
进入更新功能: 更新
启动,测试
- 登陆成功后,没有权限的标签就看不见啦~

控制台打印:
本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/373c6e0ca4.html
