- 官方 【总结】 shiro 常用依赖
官方教程
参考
- WebFilter、FilterRegistrationBean:整合filter的两种方式
- 自定义登录出两种方式
- role,perms 配置: shiroFilter配置详解
- Shiro完整教程 附带各种配置
- Apache Shiro 是 Java 的一个安全(权限)框架。
- Shiro 可以非常容易的开发出足够好的应用,其不仅可以在 JavaSE 环境,也可以用在 JavaEE 环境
- Shiro 可以完成:认证、授权、加密、会话管理、与Web集成、缓存等。
- 下载:http://shiro.apache.org/
功能简介
四个主要功能
- Authentication:认证
- Authorization:授权
- Session Manager:会话管理
- Cryptography:加密
Shiro 架构
- Subject: 一个“门面”,在 shiro 内部,可以视作 “用户”
- SecurityManager: 核心
- Realm:类似“DAO”。
HelloWorld
参考:官方教程 - Your First Apache Shiro Application
- 下载官方提供的模板: https://github.com/apache/shiro
(下面参考到的部分:shiro/samples/quickstart
)
<mark>创建 Maven 项目</mark>
依赖
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
Quickstart.java
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
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) {
log.info("My First Apache Shiro Application");
// 用ini文件,通过factory获取securityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 获取当前的 Subject
Subject currentUser = SecurityUtils.getSubject();
// 测试使用 session
// 1.获取 session
Session session = currentUser.getSession();
// 2.往 session 中放值
session.setAttribute("someKey", "aValue");
// 2.从 session 中取值
Object value = session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("----> Retrieved the correct Value! [{}]", value);
}
// 测试当前的用户是否已经被认证,即是否已经登录
// 调用 Subject 的 isAuthenticated
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为 UsernamePasswordToken 对象
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
// rememberMe
token.setRememberMe(true);
try {
// 执行登录
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("账号 {} 不存在", token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("账号 {} 密码错误", token.getPrincipal());
} catch (LockedAccountException lae) {
log.info("账号 {} 被锁定", token.getPrincipal());
}
// ... catch more exceptions here (maybe custom ones specific to your application?)
catch (AuthenticationException ae) {
// 所有认证时异常的父类
// unexpected condition? error?
}
}
// 告知登录成功
log.info("----> User [{}] logged in successfully.", currentUser.getPrincipal());
// 测试是否有某一个角色,调用 Subject 的 hasRole 方法。
if(currentUser.hasRole("schwartz")) {
log.info("----> May the Schwartz be with you!");
}else {
log.info("----> Hello, mere mortal.");
}
// 测试用户是否有某个行为,调用 Subject 的 isPermitted 方法
if(currentUser.isPermitted("lightsaber:weild")) {
log.info("You may use a lightsaber ring. Use it wisely.");
}else{
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
// 测试用户是否具备某一个行为。(相对上面的,这个更具体)
if(currentUser.isPermitted("user:delete:zhangsan")) {
log.info("可以在“用户”中“删除”“张珊”");
}else{
log.info("Sorry, 你不允许删除我心爱的张珊");
}
// 执行登出,调用 Subject 的 Logout() 方法
currentUser.logout();
if(currentUser.isAuthenticated()) {
log.info("登出失败");
}else{
log.info("登出成功");
}
System.exit(0);
}
}
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:*
# 用户:行为:id ==> goodguy“角色”可以在“用户”中“删除”“张珊”
goodguy = user:delete:zhangsan
集成 SpringBoot
需要什么依赖,官网说很清楚了:http://shiro.apache.org/download.html
<mark>创建SpringBoot项目</mark>
<mark>pom.xml</mark>
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-shiro-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-shiro-spring</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
shiro 配置类
package com.example.demoshirospring.config;
import jdk.nashorn.internal.parser.Token;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
public class ShiroConfig {
// 1.配置 SecurityManager(关键)
@Bean("securityManager")
@Autowired
public SecurityManager securityManager(
@Qualifier("cacheManager") CacheManager cacheManager,
@Qualifier("realm") Realm realm
) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setCacheManager(cacheManager);
manager.setRealm(realm);
return manager;
}
// 2.配置 CacheManger.
@Bean("cacheManager")
public CacheManager cacheManager() {
EhCacheManager manager = new EhCacheManager();
return manager;
}
// 3.配置Realm(关键)
@Bean("realm")
public Realm realm() {
// 直接实现了 Realm 接口的 bean
// 实际开发中,要么有现成的,要么有必要单独一个文档实现接口
return new AuthorizingRealm() {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 暂时不处理
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 密码为 pw 即可通过认证
return new SimpleAuthenticationInfo(new Object(), "pw", "realName");
}
};
}
// 4.配置 Shiro 生命周期处理器.
// 用于在实现了 Initializable 接口的 Shiro bean 初始化时调用 Initializable 接口回调(例如:CasRealm)
// 在实现了 Destroyable 接口的 Shiro bean 销毁时调用 Destroyable 接口回调(例如:DefaultSecurityManager)
// 可以自动的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法.
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
// 5.启用 IOC 容器中使用 shiro 的注解.
// 但必须在配置了 LifecycleBeanPostProcessor 之后才可以使用.
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
// 6.配置 ShiroFilter(关键)
// bean 名必须和下面 DelegatingFilterProxy 构造时传入的 beanName 一致
@Bean("shiroFilter")
@Autowired
public ShiroFilterFactoryBean shiroFilterFactoryBean(
@Qualifier("securityManager") SecurityManager securityManager
) {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
// 检测到用户未登录后,转跳的页面(通常为登录页URL)
factory.setLoginUrl("/index");
// 验证成功的后的默认转跳路径(若有指定路径,则不走此默认路径)
factory.setSuccessUrl("/index");
// 没有访问权限的转跳路径
factory.setUnauthorizedUrl("/unauthorizedUrl");
// 配置访问这些页面的权限
// (本质上,key是URL,value是Filter链)
/* anon 可以被匿名访问 authc 必须认证后,才可以访问的页面 user 必须荣有 记住我 功能才能用 perms 拥有对某个资源的权限才能访问 roles 荣有某个角色权限才能访问 ... 以上定义的枚举类为:org.apache.shiro.web.filter.mgt.DefaultFilter */
Map<String, String> map = factory.getFilterChainDefinitionMap();
// 必须加 "/" 斜杠
map.put("/favicon.ico", "anon");
map.put("/login", "anon");
map.put("/index", "anon");
// 表示该 uri 需要认证用户拥有 admin 角色才能访问(测试 setUnauthorizedUrl 拦截)
map.put("/user", "roles[user]");
map.put("/logined", "authc");
map.put("/**", "authc");
return factory;
}
// 7.注册自定义Filter (扩展)
/* @Bean @Autowired public FilterRegistrationBean<DelegatingFilterProxy> logoutFilterRegistration( @Qualifier("securityManager") SecurityManager securityManager ) { LogoutFilter filter = new LogoutFilter(); // 登出后转跳的 URL filter.setRedirectUrl("/index"); // 把 servlet 容器中的 filter 同 spring 容器中的 bean 关联起来 FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean<>(); DelegatingFilterProxy proxy= new DelegatingFilterProxy(filter); registration.setFilter(proxy); // 注册的拦截 拦截的URL registration.addUrlPatterns("/logout"); registration.setOrder(1); return registration; }*/
}
Controller
package com.example.demoshirospring.controller;
import javafx.css.SimpleStyleableStringProperty;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ShiroController {
@RequestMapping("/login")
public String login() {
AuthenticationToken token = new UsernamePasswordToken(
"a"
, "pw");
SecurityUtils.getSubject().login(token);
return "login";
}
@RequestMapping("/index")
public String index() {
Subject subject = SecurityUtils.getSubject();
return "首页,是否登录:" + subject.isAuthenticated();
}
@RequestMapping("/unauthorizedUrl")
public String unauthorizedUrl() {
return "unauthorizedUrl";
}
@RequestMapping("/logout")
public String logout() {
SecurityUtils.getSubject().logout();
return "logout ok!";
}
//需要 user 用户权限
@RequestMapping("/user")
public String list() {
return "user role ";
}
//需要 登录才可访问
@RequestMapping("/logined")
public String getA() {
return "has login";
}
}
测试
登录前
-
http://localhost:8080/user 【需要user角色】
登录后
- http://localhost:8080/index
- http://localhost:8080/user 【需要user角色】
- http://localhost:8080/logined 【需要登录】
- http://localhost:8080/logout