SpringBoot-09 Shiro
SpringBoot-09 Shiro
Hello Shiro
可以先创建一个最单纯的Maven项目。
1.导入依赖
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
这里我导入的依赖基本上都是最新版,如果想要别的版本可以自行搜索导入
2.创建log4j.properties
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
# General Apache libraries
log4j.logger.org.apache=WARN
# Spring
log4j.logger.org.springframework=WARN
# Default Shiro logging
log4j.logger.org.apache.shiro=INFO
# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
3.创建shiro.ini
这里第一次创建ini文件,会让你选择,可以选择txt文件,创建成功后会提示你创下载一个ini插件:
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
4.创建Quickstart
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// The easiest way to create a Shiro SecurityManager with configured
// realms, users, roles and permissions is to use the simple INI config.
// We'll do that by using a factory that can ingest a .ini file and
// return a SecurityManager instance:
// Use the shiro.ini file at the root of the classpath
// (file: and url: prefixes load from files and urls respectively):
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
// for this simple example quickstart, make the SecurityManager
// accessible as a JVM singleton. Most applications wouldn't do this
// and instead rely on their container configuration or web.xml for
// webapps. That is outside the scope of this simple quickstart, so
// we'll just do the bare minimum so you can continue to get a feel
// for things.
SecurityUtils.setSecurityManager(securityManager);
// Now that a simple Shiro environment is set up, let's see what you can do:
// get the currently executing user:
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// let's login the current user so we can check against roles and permissions:
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
5.启动测试
6.可能遇到错误
解决办法:
这是因为你的依赖中的问题:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>2.0.0-alpha1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha1</version>
<scope>test</scope>
</dependency>
依赖中存在
Shiro环境搭建
对于shiro环境,一共有三个要素:
- ShiroFilterFactoryBean
- DefaultWebSecurityManager
- Realm
我们需要倒着来创建
创建一个config文件夹,和ShiroConfig、UserRealm类
public class UserRealm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
return bean;
}
//DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager();
SecurityManager.setRealm(userRealm);
return SecurityManager;
}
//创建 realm 对象
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
以上就完成了shiro的基本框架搭建。
登录拦截
1.前置准备
用来测试的话,我们需要:
add.html、index.html、login.html和一个Controller类
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
add
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
首页<br>
<a th:href="@{/add}">add</a><br>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="">
姓名:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
@Controller
public class ShiroController {
@RequestMapping({"/","/index.html"})
public String index(){
return "index";
}
@RequestMapping("/add")
public String add(){
return "add";
}
@RequestMapping("/toLogin")
public String login(){
return "login";
}
}
2.配置
上述文件页面搭建好后,测试运行应该是没什么问题的,我们开始在ShiroConfig类中配置
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
* anon:无需认证就可以访问
* authc:必须认证了才能访问
* user:必须拥有 记住我 功能才能用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String, String> map = new LinkedHashMap<>();
map.put("/add","authc");
map.put("/update","authc");
bean.setFilterChainDefinitionMap(map);
//设置登录请求
bean.setLoginUrl("/toLogin");
return bean;
}
这时候,如果没有权限,点击add会自动进入设置的登陆页面。
3.用户权限添加
添加登录Controller
@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 uae) {
model.addAttribute("msg","用户名错误");
return "login";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("msg","密码错误");
return "login";
}
}
修改UserRealm
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//用户名密码 先用下面的测试,之后可以连接数据库
String name="root";
String password="123456";
UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken;
if (!Token.getUsername().equals(name)){
return null; //自动抛出异常
}
//密码认证 shiro来做
return new SimpleAuthenticationInfo("",password,"");
}
修改登录页面
<p th:text="${msg}"></p>
<form th:action="@{/Login}">
姓名:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
<input type="submit" value="提交">
</form>
之后就可以去测试了。
整合Mybatis
1.导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
2.配置文件
application.yml:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 148729
url: jdbc:mysql://localhost:3306/mybatistest?serverTime=UTC&useUnicode=true&characterEncoding=utf-8
#druid配置
#配置初始化大小/最小/最大
initialSize: 5
minIdle: 5
maxActive: 20
#获取连接等待超时时间
maxWait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
timeBetweenEvictionRunsMillis: 60000
#一个连接在池中最小生存的时间
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: 20
#监控统计拦截的filters,stat:监控统计;log4j:日志记录;wall:防御sql注入;如果启用log4j记得添加依赖
filters: stat,wall,log4j
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
application.properties:
mybatis.type-aliases-package=com.zc.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
3.实体类
public class User {
private Integer id;
private String name;
private String pwd;
// Get/Set方法
// toString()方法
// 有参/无参方法
}
4.创建mapper接口
创建一个mapper文件夹,UserMapper:
@Mapper
@Repository
public interface UserMapper {
User queryUserByName(String name);
}
5.mapper.xml
这个文件的位置按照之前配置:
在resources/mapper下
mybatis.mapper-locations=classpath: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.zc.mapper.UserMapper">
<select id="queryUserByName" resultType="User">
select * from user where name=#{name};
</select>
</mapper>
5.修改UserRealm
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken; //用户名密码
User user = userMapper.queryUserByName(Token.getUsername());
String name=user.getName();
String password=user.getPwd();
if (user==null){
return null;
}
//密码认证 shiro来做
return new SimpleAuthenticationInfo("",password,"");
}
就可以进行测试了。
授权、权限管理
创建一个未经授权无法访问Controlle方法:
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未经授权无法访问此页面";
}
修改ShiroConfig:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
Map<String, String> map = new LinkedHashMap<>();
//拦截
map.put("/add","authc");
map.put("/update","authc");
//授权
map.put("/add","perms[user:add]");
bean.setFilterChainDefinitionMap(map);
//设置登录请求
bean.setLoginUrl("/toLogin");
//拦截页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
测试
这个时候我已经给/add方法添加了user:add权限,只有有这个权限的用户才可以登录
这时候测试的话,会发现,即时登录成功通过了拦截,add页面也会显示未经授权无法访问此页面
在UserRealm中授权
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
return info;
}
现在,只要登陆的用户,都会被添加user:add权限。
也可以进行数据库连接:
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 数据库中加入一个权限字段,可以这样查找
Subject subject = SecurityUtils.getSubject();
//认证部分传入,可以获取
User principal = (User) subject.getPrincipal();
info.addStringPermission(user.getxxx());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken; //用户名密码
User user = userMapper.queryUserByName(Token.getUsername());
String password=user.getPwd();
if (user==null){
return null;
}
//这里修改了,传入了user,在授权部分可以获取
return new SimpleAuthenticationInfo(user,password,"");
}
整合Thymeleaf
1.导入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2.修改index.html
导入头文件
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro" >
内部文件
<body>
首页<br>
<a th:href="@{/toLogin}">登录</a><br>
<div shiro:hasPermission="user:add">
<a th:href="@{/add}">add</a><br>
</div>
<a th:href="@{/update}">update</a>
</body>
这种,是最简单的页面整理,不同权限会显示不同页面,没有权限部分功能不展示。
个人博客为:
MoYu's HomePage
MoYu's Gitee Blog