shiro
shiro核心API
subject:用户主题
SecurityManager: 安全管理器 管理对象 封装shiro功能
Realm:shiro连接数据库的桥梁,数据的提供者
目录结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9rX4vr7-1629448963989)(shiro.assets/image-20210820110830949.png)]
SpringBootApplication.java
@SpringBootApplication
@MapperScan(value = {"com.gxh.modules.**.mapper*"})
public class ShiroApplication {
public static void main(String[] args) throws Exception{
Long s1 = System.currentTimeMillis();
ConfigurableApplicationContext application = SpringApplication.run(ShiroApplication.class, args);
Long s2 = System.currentTimeMillis();
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
System.out.println("=======================================>");
System.out.println("启动耗时=======>"+(s2-s1));
System.out.println("ip:http://localhost:" + port + path);
System.out.println("serve start-up success");
System.out.println("=======================================>");
}
}
application.yml
server:
port: 8089
servlet:
context-path: /
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/#{你的数据库}?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username: #{你的账号}
password: #{你的密码}
resources:
static-locations: classpath:/static/,classpath:/templates/
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mybatis-plus:
mapper-locations: classpath*:com/gxh/modules/**/xml/*Mapper.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 关闭mybatis-plus启动banner
global-config:
banner: false
#logging:
# level:
# com.lxito.mapper: debug
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<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>2.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.14</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
</dependency>
<!-- mybatis-plus相关依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--加入验证码随机数-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.5.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!--生产xml-->
<!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉。 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.html</include>
<include>**/*.css</include>
<include>**/*.js</include>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
<!--次pom为项目所需要用到的依赖以及 buil-->
整合shiro
/***
* 创建一个过滤器工厂
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactory= new ShiroFilterFactoryBean();
//安全管理器
filterFactory.setSecurityManager(securityManager);
return filterFactory;
}
/***
* 创建安全管理器
*/
@Bean
public DefaultWebSecurityManager securityManager(ShiroRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//给管理器赋值realm
securityManager.setRealm(realm);
return securityManager;
}
/**
* 配置自定义realm
*/
@Bean
public ShiroRealm userRealm(){
ShiroRealm realm = new ShiroRealm();
return realm;
}
3.1 编写shiro相关配置类
package com.gxh.modules.config;
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;
/***
* shiro 配置类
*/
@Configuration
public class ShiroConfig {
/***
* 创建一个过滤器工厂
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactory= new ShiroFilterFactoryBean();
//安全管理器
filterFactory.setSecurityManager(securityManager);
return filterFactory;
}
/***
* 创建安全管理器
*/
@Bean
public DefaultWebSecurityManager securityManager(ShiroRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//给管理器赋值realm
securityManager.setRealm(realm);
return securityManager;
}
/**
* 配置自定义realm
*/
@Bean
public ShiroRealm userRealm(){
ShiroRealm realm = new ShiroRealm();
return realm;
}
}
3.2使用shiro内置拦截器实现页面拦截
LinkedHashMap<String,String> map = new LinkedHashMap<String, String>();
/**
* shiro内置过滤器
* 常用过滤器
* anon:不需要认证就可以直接访问
* authc: 必须需要认证才可以访问的页面
* user: 如果使用了记住我的功能可以直接访问页面
* perms: 必须得到资源权限才可以访问
* role: 必须得到角色权限才可以访问
*/
//假设用户主页都可以访问,增删该必须认证后才可以访问
map.put("/user","anon"); //不需要认证
map.put("/add","authc"); //需要认证
map.put("/update","authc"); //需要认证
map.put("/delete","authc"); //需要认证
filterFactory.setFilterChainDefinitionMap(map);
//设置登录页
filterFactory.setLoginUrl("/login");
认证获取数据库中的数据进行比对
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.findAllByName(token.getUsername());
//判断
if (ObjectUtils.isEmpty(user)){
return null;
}
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
授权
//只有达到权限后才可以执行
map.put("/add","perms[user:insert]");
map.put("/update","perms[user:update]");
//在shiroconfig中将insert和update设置为 "必须得到资源权限才可以访问"
/** * 授权 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("授权"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //添加资源授权的字符串 保证配置一样 info.addStringPermission("user:insert");// info.addStringPermission("user:update"); return info; }
controller层
@RequestMapping("/login")public String index(){ return "pages/login";}@RequestMapping("/add")public String addUser(){ return "pages/add";}@RequestMapping("/update")public String updateUser(){ return "pages/update";}@RequestMapping("/delete")public String deleteUser(){ return "pages/delete";}@RequestMapping("/user")public String userIndex(){ return "pages/user";}/** * 登录 */@RequestMapping("/loginIn")public String loginIn(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); //跳转到成功页面 return "pages/user"; }catch (UnknownAccountException e){ model.addAttribute("msg","用户名不存在"); return "pages/login"; }catch (IncorrectCredentialsException e){ model.addAttribute("msg","密码不正确"); return "pages/login"; }}/*** * 设置未授权页面 */@RequestMapping("/noPermission")public String noPermission(){ return "pages/noPermission";}
mapper层
package com.gxh.modules.mapper;import com.gxh.modules.po.User;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;@Repositorypublic interface SysUserMapper { /*** * 根据用户名查询信息 */ public User findAllByName(@Param("username") String username);}
mapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.gxh.modules.mapper.SysUserMapper"> <select id="findAllByName" parameterType="java.lang.String" resultType="com.gxh.modules.po.User"> select * from `sys_user` where username = #{username} </select></mapper>
页面展示层
login.html
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head> <meta charset="UTF-8"> <title>登录</title></head><body><h2 th:text="${msg}"></h2><h1>登录功能</h1><form method="post" action="loginIn"> 用户名:<input type="text" name="username"><br/> 密 码: <input type="password" name="password"><br/> <input type="submit" value="登录"></form></body></html>
user.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body> 用户管理页面 <a href="add">用户添加</a> <a href="update">用户修改</a> <a href="delete">用户删除</a></body></html>
add.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body>用户添加</body></html>
update.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body>用户修改</body></html>
delete.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body>用户删除</body></html>
nopermission.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>permission</title></head><body> 未授权;若想拥有此权限,请联系管理员配置权限!</body></html>
动态获取权限
修改数据库表结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VW6JgH9g-1629448963991)(shiro.assets/image-20210820100852422.png)]
执行授权位置修改动态权限菜单
/** * 授权 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("授权"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //添加资源授权的字符串 保证配置一样 //info.addStringPermission("user:insert"); //info.addStringPermission("user:update"); //动态查询数据库权限 Subject subject = SecurityUtils.getSubject(); //获取对象 User user = (User) subject.getPrincipal(); //获取用户对象的权限 info.addStringPermission(user.getPermission()); return info; }
/** * 认证 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("认证"); UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; User user = userService.findAllByName(token.getUsername()); //判断 if (ObjectUtils.isEmpty(user)){ return null; } return new SimpleAuthenticationInfo(user,user.getPassword(),""); }
1.创建表结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q4pKoG67-1629448963992)(shiro.assets/image-20210820104552401.png)]
2.功能模拟
账号 | 密码 | 备注 |
---|---|---|
admin | 123456 | 所有权限 |
gxh | 123456 | 具有用户管理权限 |
other | 123456 | 具备添加,修改权限 |
3.创建想关的实体类对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xkG4SVHf-1629448963993)(shiro.assets/image-20210820111845271.png)]
4.导入数据库五张表以及数据信息
注:源码放在最后,请根据目录结构自行创建,所需要的资源请继续往下看!
5.Mapper层接口
/*** * 根据用户名查询信息 */ public User findAllByName(@Param("username") String username); /** * 根据用户id查询信息 * @param id */ User queryUserById(@Param("id")Integer id); /*** * 根据id查询权限 和 url标识 * @param id */ List<User> findUrlAndPermByUserId(@Param("id") Integer id); /*** * 查询按钮级别的权限 * @param id */ List<Menu> queryMenuUrlAndPermUserId(@Param("id") Integer id);
mapper实现层
<select id="findAllByName" parameterType="java.lang.String" resultType="com.gxh.modules.po.User"> select * from `user` where username = #{username} </select> <!--根据用户id查询信息--> <select id="queryUserById" parameterType="java.lang.Integer" resultType="com.gxh.modules.po.User"> select * from `user` where id = #{id} </select> <!--根据id查询权限 和 url标识--> <select id="findUrlAndPermByUserId" parameterType="java.lang.Integer" resultType="com.gxh.modules.po.User"> SELECT DISTINCT url, permission FROM `user` u LEFT JOIN user_role ur ON ur.user_id = u.id LEFT JOIN role r ON r.id = ur.role_id LEFT JOIN role_menu rm ON rm.role_id = r.id LEFT JOIN menu m ON m.id = rm.menu_id WHERE m.type in (1,0) <if test="id != null and id != ''"> and u.id = #{id} </if> </select> <!--查询按钮级别的权限--> <select id="queryMenuUrlAndPermUserId" parameterType="java.lang.Integer" resultType="com.gxh.modules.po.Menu"> SELECT DISTINCT url, permission FROM `menu` m LEFT JOIN role_menu rm ON rm.menu_id = m.id LEFT JOIN `role` r ON rm.id = r.id LEFT JOIN user_role ur ON ur.role_id = r.id LEFT JOIN `user` u ON u.id = ur.user_id WHERE m.type = '2' <if test="id != null and id != ''"> and u.id = #{id} </if> </select>
service层
/*** * 根据用户名查询信息 */ public User findAllByName(String username); /** * 根据用户id查询信息 * @param id */ User queryUserById(Integer id); /*** * 根据id查询权限 和 url标识 * @param id */ List<User> findUrlAndPermByUserId(Integer id); /*** * 查询按钮级别的权限 * @param id */ List<Menu> queryMenuUrlAndPermUserId(Integer id);
ServiceImpl
@Service@RequiredArgsConstructor(onConstructor = @__(@Autowired))public class SysUserServiceImpl implements SysUserService { private final SysUserMapper userMapper; @Override public User findAllByName(String username) { return userMapper.findAllByName(username); } @Override public User queryUserById(Integer id) { return userMapper.queryUserById(id); } @Override public List<User> findUrlAndPermByUserId(Integer id) { return userMapper.findUrlAndPermByUserId(id); } @Override public List<Menu> queryMenuUrlAndPermUserId(Integer id) { return userMapper.queryMenuUrlAndPermUserId(id); }}
编写全向控制想关工具类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnbOdM2V-1629448963994)(shiro.assets/image-20210820151859242.png)]
注: 此类若想在其他类中注入一定要在类上加@Component(“shiroUtils”) 注解
@Component("shiroUtils")
public class ShiroUtils {
@Autowired
private SysUserService userService;
/***
* 获取过滤配置
*/
public Map<String,String> perms(){
LinkedHashMap<String,String> map = new LinkedHashMap<String,String>();
/**
* shiro内置过滤器
* 常用过滤器
* anon:不需要认证就可以直接访问
* authc: 必须需要认证才可以访问的页面
* user: 如果使用了记住我的功能可以直接访问页面
* perms: 必须得到资源权限才可以访问
* role: 必须得到角色权限才可以访问
*/
//假设用户主页都可以访问,增删该必须认证后才可以访问
map.put("login","anon");
map.put("/loginIn","anon"); //不需要认证
//只有达到权限后才可以执行
map.put("/**","authc"); //需要认证
return map;
}
/**
* 获取权限列表
*/
public List<Menu> queryPerm(Integer userId){
List<Menu> menuList = userService.queryMenuUrlAndPermUserId(userId);
return menuList;
}
然后再ShiroConfig和ShiroRealm中可以这样写
ShiroConfig
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s5KJhTyD-1629448963994)(shiro.assets/image-20210820152207871.png)]
注入ShiroUtils
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-93tQm90R-1629448963995)(shiro.assets/image-20210820152235705.png)]加入ShiroDialect
/**
* 配置shiro和thymeleaf标签
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
ShiroRealm
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kpl6o7SL-1629448963995)(shiro.assets/image-20210820152251615.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9wXMv5FK-1629448963996)(shiro.assets/image-20210820152309867.png)]
Springboot启动类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mdFbF2Cp-1629448963997)(shiro.assets/image-20210820154216322.png)]
页面菜单权限的控制
创建对应的功能页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dxRGgAzZ-1629448963997)(shiro.assets/image-20210820162004105.png)]
Contoller层
@Controller
public class BaseController {
@RequestMapping("/login")
public String index(){
return "pages/login";
}
@RequestMapping("/addUser")
public String addUser(){
return "pages/page/addUser";
}
@RequestMapping("/updateUser")
public String updateUser(){
return "pages/page/updateUser";
}
@RequestMapping("/deleteUser")
public String deleteUser(){
return "pages/page/deleteUser";
}
@RequestMapping("/user")
public String userIndex(){
return "pages/page/user";
}
@RequestMapping("/addDept")
public String addDept(){
return "pages/page/addDept";
}
@RequestMapping("/updateDept")
public String updateDept(){
return "pages/page/updateDept";
}
@RequestMapping("/deleteDept")
public String deleteDept(){
return "pages/page/deleteDept";
}
@RequestMapping("/dept")
public String dept(){
return "pages/page/dept";
}
}
前台页面处理
加入相关约束
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.w3.org/1999/xhtml">
权限标签
<a href="addDept" shiro:hasPermossion="dept:insert">添加</a>
<a href="updateDept" shiro:hasPermossion="dept:update">修改</a>
<a href="deleteDept" shiro:hasPermossion="dept:delete">删除</a>
注意点:
dept:insert dept:update dept:delete
和数据库表菜单信息表中的权限标识符保持一直即可
以上只是简单的权限控制,并没有才用到密码的散列加密等,如果想学可以看这个连接
一片非常适合小白的shiro教程