文章目录
1 shiro简介
2 简单使用
2.1 业务说明
2.2 shiro集成boot
2.2.1 建立项目shiro
2.2.2 简单演示authc
2.2.3 编写登录逻辑
2.2.4 编写授权逻辑
2.3 shiro动态数据(boot集成mybatis)
2.3.1 基础搭建
2.3.2 采用数据库登录验证
2.3.3 采用数据库进行授权操作
2.4 thymeleaf整合shiro
1 shiro简介
subject: 用户主体
SecurityManager:安全管理器
Realm:Shiro连接数据的桥梁
2 简单使用
2.1 业务说明
业务需求:实现登录认证和资源访问的授权
技术选型:
spring boot:2.3.0.RELEASE
jdk:1.8
shiro:1.5.3
mysql:5.5.27
2.2 shiro集成boot
2.2.1 建立项目shiro
1 新建spring项目,开发工具选择lombok
2 修改pom,增加shiro依赖 org.apache.shiro.shiro-spring.version:1.5.3
3 增加实现了AuthorizingRealm的具体realm类
package cn.ithzp.shiro.config;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("执行授权逻辑");
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("执行认证逻辑");
return null;
}
}
package cn.ithzp.shiro.config;
import lombok.extern.slf4j.Slf4j;
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 java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@Configuration
public class ShiroConfig {
@Bean("userRealm")
public UserRealm getRealm() {
log.info("获取继承了AuthorizingRealm的具体realm对象");
return new UserRealm();
}
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(
@Qualifier("userRealm") UserRealm userRealm
) {
log.info("获取关联具体realm的securityManager安全管理器");
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(
@Qualifier("securityManager") DefaultWebSecurityManager securityManager
) {
log.info("获取shiro内置过滤器");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
/**
* shiro内置过滤器包含anon/authc/user/perms/role
* anon:无需认证
* authc:必须认证
* user:如果使用rememberme可以直接访问
* perms:必须有资源权限才可以访问
* role:必须有角色权限才可以放
*/
Map filterMap = new LinkedHashMap<>();
filterMap.put("/*", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
}
2.2.2 简单演示authc
1 修改pom文件,增加web和thymeleaf依赖 spring-boot-starter-web和spring-boot-starter-thymeleaf
2 增加权限演示类CommonController
package cn.ithzp.shiro.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@Slf4j
public class CommonController {
@RequestMapping("/bridge")
public String bridge(Model model) {
log.info("进入权限跳转页面");
model.addAttribute("name", "权限跳转页面");
return "bridge";
}
}
3 增加前端页面resources/templates/bridge.html
权限跳转页面
进入用户添加功能:用户添加
进入用户更新功能:用户更新
访问http://localhost:8080/bridge
- 结果1:ShiroConfig中取消注解@Configuration,可以正常访问
- 结果2:ShiroConfig中增加注解@Configuration,页面404,跳转到http://localhost:8080/login.jsp
2.2.3 编写登录逻辑
1 修改ShiroConfig,增加登录权限,增加访问失败后跳转登录页面
// 需要放到authc的前面
filterMap.put("/login", "anon");
filterMap.put("/*", "authc");
shiroFilterFactoryBean.setLoginUrl("/toLogin");
2 修改CommonController,增加跳转登录页方法和登录验证方法
@RequestMapping("/toLogin")
public String toLogin() {
log.info("跳转登录页");
return "/login";
}
@RequestMapping("/login")
public String login(String userName, String passwd, Model model) {
log.info("进入登录操作,参数userName={},passwd={}", userName, passwd);
// 1 获取用户主体
Subject subject = SecurityUtils.getSubject();
// 2 封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(userName, passwd);
// 3 执行登录
try {
subject.login(token);
log.info("登录成功");
return "redirect:bridge";
} catch (UnknownAccountException u) {
model.addAttribute("msg", "用户不存在");
return "login";
} catch (IncorrectCredentialsException i) {
log.info("密码错误");
model.addAttribute("msg", "密码错误");
return "login";
}
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("执行认证逻辑");
String userName = "a";
String password = "b";
// 1 用户名验证
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
if (!StringUtils.equals(userName, token.getUsername())) {
log.info("用户不存在");
// 会抛出UnknownAccountException
return null;
}
// 2 密码验证
return new SimpleAuthenticationInfo("", password, "");
}
4 增加前端页面resources/templates/login.html
登录页面
登录
1 浏览器访问http://localhost:8080/bridge
- 结果跳转到http://localhost:8080/toLogin
2 输入错误用户名提示用户不存在
3 输入错误密码提示密码错误
4 输入正确用户名密码,页面重定向到http://localhost:8080/bridge
2.2.4 编写授权逻辑
1 修改ShiroConfig,增加资源权限,设置未授权跳转页
filterMap.put("/add", "perms[user:add]");
filterMap.put("/update", "perms[user:update]");
filterMap.put("/*", "authc");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
2 修改CommonControler,增加方法add/update/unauth
@RequestMapping("/add")
public String add() {
log.info("执行用户添加");
return "/user/add";
}
@RequestMapping("/update")
public String update() {
log.info("执行用户更新");
return "/user/update";
}
@RequestMapping("/unauth")
@ResponseBody
public String unauth() {
log.info("未授权");
return "亲,您还未被授权";
}
3 增加前端页面/templates/user/add.html和update.html
用户添加页面
用户添加
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("执行授权逻辑");
// 对资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addStringPermission("user:add");
info.addStringPermission("user:update");
return info;
}
1 访问http://localhost:8080/toLogin后执行登录操作
- 结果会跳转到http://localhost:8080/bridge
2 点击页面中用户添加,跳转http://localhost:8080/unauth,提示未授权
3 点击页面中用户更新,跳转http://localhost:8080/update,提示更新
2.3 shiro动态数据(boot集成mybatis)
2.3.1 基础搭建
1.1.22
5.1.42
1.1.1
2 增加配置/resources/application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=cn.ithzp.shiro.domain
mybatis.mapper-locations=classpath:mapper/*.xml
# 打印sql日志
loggin.level.cn.ithzp.shiro.mapper=debug
package cn.ithzp.shiro.domain;
import lombok.Data;
@Data
public class UserDomain {
private int id;
private String userName;
private String passwd;
private String perms;
}
4 增加持久层cn.ithzp.shiro.mapper.UserMapper
package cn.ithzp.shiro.mapper;
import cn.ithzp.shiro.domain.UserDomain;
public interface UserMapper {
UserDomain findByUserName(String userName);
}
5 增加xml映射src/main/resources/mapper/UserMapper.xml
select id, user_name as userName, passwd from user where user_name=#{value}
6 增加业务处理service和serviceImpl
package cn.ithzp.shiro.service.impl;
import cn.ithzp.shiro.domain.UserDomain;
import cn.ithzp.shiro.mapper.UserMapper;
import cn.ithzp.shiro.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public UserDomain findByUserName(String userName) {
return userMapper.findByUserName(userName);
}
}
@SpringBootApplication
@MapperScan("cn.ithzp.shiro.mapper")
2.3.2 采用数据库登录验证
// 1 用户名验证
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
UserDomain dbUser = userService.findByUserName(token.getUsername());
log.info("数据库用户信息为:{}", dbUser);
if (dbUser == null) {
log.info("用户不存在");
// 会抛出UnknownAccountException
return null;
}
// 2 密码验证
return new SimpleAuthenticationInfo("", dbUser.getPasswd(), "");
访问http://localhost:8080/toLogin后执行登录操作,输入数据库用户名和密码可以正常跳转
2.3.3 采用数据库进行授权操作
1 修改UserRealm,修改登录认证后密码验证,将对象传递到授权方法中
// 2 密码验证
return new SimpleAuthenticationInfo(dbUser , dbUser.getPasswd(), "");
2 在service/impl/mapper/xml中增加查询授权方法
select id, user_name as userName, passwd, perms from user where id=${value}
3 修改UserRealm,修改授权方法,权限从数据库动态获取
// 对资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 获取subject对象
Subject subject = SecurityUtils.getSubject();
UserDomain loginUser = (UserDomain) subject.getPrincipal();
UserDomain dbUser = userService.findById(loginUser.getId());
info.addStringPermission(dbUser.getPerms());
return info;
1 访问http://localhost:8080/toLogin后采用jack登录,
- 跳转http://localhost:8080/bridge
2 点击“用户添加”,跳转到http://localhost:8080/add,提示用户添加
3 点击“用户更新”,跳转到http://localhost:8080/unauth,提示未授权
2.4 thymeleaf整合shiro
2.0.0
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
3 修改Bridge.html,增加shiro权限
权限跳转页面
访问登录页http://localhost:8080/toLogin
- 采用jack登录,只能查看用户添加
- 采用tom登录,只能查看用户更新