您的第一个 Apache Shiro 应用程序
引入依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.tianfan</groupId>
<artifactId>shiroTest</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.12.0</version>
</dependency>
<!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding
in this example app. See https://www.slf4j.org for more info. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
lombok.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="30 seconds">
<!-- 彩色日志依赖 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 日志文件储存地址, 通过application配置文件传入 -->
<!-- <springProperty scope="context" name="LOG_PATH" source="logback.logDir" />-->
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_PATH" value="./logs"/>
<!-- 控制台彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 日志文件日志格式 -->
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%15t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 按照每天及大小生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_PATH}/my-blog-site.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件最大大小-->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 过滤级别,
如果想分类生成日志文件的话(分成debug、info、error等三个日志文件, 每个文件只记录自己级别的日志),
1. 直接把这个 <appender> 复制三分改一下 FileNamePattern 和 name.
2. 把 <filter> 注释去掉改一下 level 就可以了
-->
<!-- <filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!-- <level>info</level>-->
<!-- <onMatch>ACCEPT</onMatch>-->
<!-- <onMismatch>DENY</onMismatch>-->
<!-- </filter>-->
</appender>
<!-- 日志输出级别
如果使用springProfile, 就需要在application配置文件中通过 spring.profiles.active=dev 来指定环境,
也可以直接去掉 <springProfile> 这个标签或者把它整个注释掉
-->
<springProfile name="dev,prod">
<logger name="org.springframework" level="INFO" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="com.netflix" level="INFO" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="com.pro.test" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="java.sql" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</logger>
<!--项目包的路径-->
<logger name="com.pro" level="DEBUG,INFO" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</logger>
</springProfile>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
先不涉及web的,想看web的请往后面看:
模拟数据: shiro.ini:
# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================
# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
package org.tianfan;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Tutorial {
private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
public static void main(String[] args) {
log.info("My First Apache Shiro Application");
//1.拿到 IniSecurityManagerFactory ini的管理工具
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.拿到安全管理
SecurityManager securityManager = factory.getInstance();
//3.配置一下SecurityUtils类,以后各种操作就靠这个类
SecurityUtils.setSecurityManager(securityManager);
//拿到主体,当前正在与软件交互的东西
Subject currentUser = SecurityUtils.getSubject();
//获得Session
Session session = currentUser.getSession();
//这个不用提示了吧
session.setAttribute( "someKey", "aValue" );
System.exit(0);
}
}
使用Shiro
这是一个特定于 Shiro 的实例,它提供了您习惯于常规 HttpSessions 的大部分功能,但有一些额外的好处和一个很大的区别:它不需要 HTTP 环境!Session
如果部署在 Web 应用程序内,则默认情况下将基于 Web 应用程序。但是,在非 Web 环境中,比如这个简单的教程应用程序,Shiro 将默认自动使用其企业会话管理。这意味着,无论部署环境如何,您都可以在任何层的应用程序中使用相同的 API!这打开了一个全新的应用程序世界,因为任何需要会话的应用程序都不需要强制使用 或 EJB 有状态会话 Bean。而且,任何客户端技术现在都可以共享会话数据。Session
HttpSession
HttpSession
所以现在你可以获得一个和他们的.那些真正有用的东西呢,比如检查他们是否被允许做一些事情,比如检查角色和权限?Subject
Session
好吧,我们只能对已知用户进行这些检查。我们上面的实例代表当前用户,但谁是当前用户?好吧,他们是匿名的 - 也就是说,直到他们至少登录一次。所以,让我们这样做:Subject
if ( !currentUser.isAuthenticated() ) {
//currentUser.isAuthenticated()要是为true就是登录了,加了非,就是没有登录的就往下走
//这个lonestarr和vespa是要自己输入的数据,用户名,密码之类的,从前端表格获取的,都行
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//表示开启“记住我”功能,即在下次登录时,系统会自动填充上一次登录时的用户名和密码,方便用户快速登录系统。
token.setRememberMe(true);
//currentUser.login(token)是一个登录方法,它将用户的凭据与系统中存储的用户信息进行比较,如果凭据匹配,则将用户标识为已验证用户。在这个例子中,token是一个UsernamePasswordToken对象,它包含了用户的用户名和密码。如果用户的凭据被验证成功,那么用户将被标识为已验证用户,否则将抛出异常。
currentUser.login(token);
}
那么如何管理这些异常呢?答案是捕获异常
try {
currentUser.login( token );
//if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
//username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
//password didn't match, try again?
} catch ( LockedAccountException lae ) {
//account for that username is locked - can't login. Show them a message?
}
... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
//unexpected condition - error?
}
好了,现在让我们搞一个完整的项目:
完整代码:
package org.tianfan;
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.subject.Subject;
import org.apache.shiro.util.Factory;
public class Tutorial {
public static void main(String[] args) {
// 读取INI配置文件并创建SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 获取当前用户
Subject currentUser = SecurityUtils.getSubject();
// 登录
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
try {
currentUser.login(token);
System.out.println("登录成功!");
} catch (UnknownAccountException uae) {
System.out.println("未知账户!");
} catch (IncorrectCredentialsException ice) {
System.out.println("密码不正确!");
} catch (LockedAccountException lae) {
System.out.println("账户已锁定!");
} catch (AuthenticationException ae) {
System.out.println("认证失败!");
}
}
// 检查用户是否拥有某个角色
if (currentUser.hasRole("goodguy")) {
System.out.println("您拥有goodguy角色!");
} else {
System.out.println("您没有goodguy角色!");
}
// 检查用户是否拥有某个权限
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
System.out.println("您拥有winnebago:drive:eagle5权限!");
} else {
System.out.println("您没有winnebago:drive:eagle5权限!");
}
// 登出
currentUser.logout();
}
}
数据文件:
# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================
# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
运行结果:
Shiro与SpringbootWeb的正式结合使用:
感觉字数太多了,下一章再讲吧