Shiro安全框架的简单入门 Shiro 是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
Shiro官方10分钟入门
1. Shiro三个核心组件
Subject Subject:即“当前操作用发户”。意味着“当前跟软件交互的东西”。·可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager SecurityManager: Shiro通过SecurityManager来管理内部组件实例,简单使用就是管理Subject了。
Realm Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。Realm实质上是一个安全相关的DAO
导入依赖
配置文件
启动类
2. Shiro相关类介绍 (1)Authentication 认证 —- 用户登录 (2)Authorization 授权 — 用户具有哪些权限 (3)Cryptography 安全数据加密 (4)Session Management 会话管理 (5)Web Integration web系统集成 (6)Interations 集成其它应用,spring、缓存框架
3.整合springboot 简单的登陆验证实例
Demo项目结构
pom.xml相关包的导入
<dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-spring</artifactId > <version > 1.5.0</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.10</version > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.0.1</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <scope > runtime</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.8</version > <scope > provided</scope > </dependency >
相关配置类以及各层实现代码
编写ShiroConfig类,主要就是要配置三个Shiro的对象
在ShiroFilterFactoryBean 中设置拦截链
package com.x2yu.springbootshiro.config;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.HashMap;import java.util.Map;@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean (@Qualifier("getDefaultWebSecurityManager" ) DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); Map<String,String> filterMap = new HashMap<>(); filterMap.put("/user/add" ,"perms[user:add]" ); filterMap.put("/user/update" ,"authc" ); shiroFilterFactoryBean.setLoginUrl("/toLogin" ); shiroFilterFactoryBean.setUnauthorizedUrl("/noauth" ); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager (@Qualifier("getUserRealm" ) UserRealm userRealm) { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(userRealm); return defaultWebSecurityManager; } @Bean public UserRealm getUserRealm () { return new UserRealm(); } }
上面的UserRealm为自定义 在Realm中和数据库交互获取用户信息 权限
package com.x2yu.springbootshiro.config;import com.x2yu.springbootshiro.pojo.User;import com.x2yu.springbootshiro.service.UserService;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.springframework.beans.factory.annotation.Autowired;public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) { System.out.println("执行授权方法" ); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.addStringPermission("user:add" ); return simpleAuthorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证方法" ); UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; User user = userService.queryUserByName(token.getUsername()); if (user == null ){ return null ; } return new SimpleAuthenticationInfo("" ,user.getPwd(),"" ); } }
User对象类
package com.x2yu.springbootshiro.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
Service层和其实现层
public interface UserService { public User queryUserByName(String name); } @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Override public User queryUserByName(String name) { return userMapper.queryUserByName(name); } }
Mapper 根据姓名查询
@Mapper @Repository public interface UserMapper { public User queryUserByName(String name); }
UserMapper.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.x2yu.springbootshiro.mapper.UserMapper" > <select id ="queryUserByName" parameterType ="String" resultType ="User" > select * from mybatis.user where name = #{name} </select > </mapper >
数据库中数据
id name pwd 1 admin 123456 2 test 111111 3 root 12345
说到了数据库就顺便把配置文件标记贴一下
application.properties
#mybatis相关配置 mybatis.type-aliases-package=com.x2yu.springbootshiro.pojo mybatis.mapper-locations=classpath:mapper/*.xml
application.yml 数据库连接相关配置
spring: datasource: username: root password: 123456 #?serverTimezone=UTC解决时区的报错 url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver 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
前端代码
前端采用thymeleaf进行了简单的测试
add 和 update中都只是简单的提示 随便什么内容可以区分都行
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首页</h1> <p th:text="${msg}"></p> <hr> <a th:href="@{/user/add}">add</a>| <a th:href="@{/user/update}">update</a> <a th:href="@{/logout}">logout</a> </body> </html>
login.html
<!DOCTYPE html> <html lang="en"xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>登陆</h1> <br> <p th:text="${msg}"></p> <form th:action="@{/login}"> <p>用户名:<input type="text" name="username"></p> <p>密码:<input type="text" name="password"></p> <p>用户名:<input type="submit"></p> </form> </body> </html>
4. 控制层 Controller
package com.x2yu.springbootshiro.controller;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;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.ResponseBody;@Controller public class MyController { @RequestMapping ({"/" ,"/index" }) public String toIndex (Model model) { model.addAttribute("msg" ,"hello,Shiro" ); return "index" ; } @RequestMapping ({"/user/add" }) public String add (Model model) { return "user/add" ; } @RequestMapping ({"/user/update" }) public String update (Model model) { return "user/update" ; } @RequestMapping ({"/toLogin" }) public String toLogin (Model model) { return "login" ; } @RequestMapping ({"/login" }) public String login (String username, String password,Model model) { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username,password); try { subject.login(token); return "index" ; } catch (UnknownAccountException e) { model.addAttribute("msg" ,"用户名不存在" ); return "login" ; } catch ( IncorrectCredentialsException ice) { model.addAttribute("msg" ,"密码错误" ); return "login" ; } } @RequestMapping ("/noauth" ) @ResponseBody public String unauthorized () { return "未授权" ; } @RequestMapping ("/logout" ) @ResponseBody public String logout () { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "logout!" ; } }
4. 简单测试 输入http://localhost:8080/
跳转首页
此时是未登录状态 点击add 和 update 都会跳转到 login 页面
id name pwd 1 admin 123456 2 test 111111 3 root 12345
从数据库中选一个登陆 如果输入密码不对提示密码错误
输入错误账号 判断为没有此账号
输入正确则可以正常访问 add 和 update
但是这里有一个问题,在 ShiroConfig中
filterMap.put("/user/add","perms[user:add]");//拥有授权可以进 filterMap.put("/user/update","authc");//认证可进
add 页面应该是有权限可进,update是登陆认证可进
授权是在UserRealm中 为了测试简单我为所有账号都授权了,实际应该查询数据库
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.addStringPermission("user:add");//授权
那我们注释掉这一段 重启
点击add 跳转了 http://localhost:8080/noauth 提示未授权 update页面正常进入
未授权页面设置在ShiroConfig中
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");//未授权页面
@RequestMapping("/noauth") @ResponseBody public String unauthorized(){ return "未授权"; }
最后点击Logout 再返回点击add或者update的时候又会跳转到登陆界面了。
贴一个正常的用户Shiro验证应该的所需要的表