1. 入门概述
1.1. 什么是Shiro?
Apache Shiro是一个功能强大且易于使用的Java安全(权限)框架。Shiro可以完成:认证、授权、加密、会话管理、与Web集成、换成等。借助Shiro可以快速轻松地保护任何应用程序————从最小的移动应用程序到最大的Web和企业应用程序。
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
Shiro官网:https://shiro.apache.org/
1.2. 基本功能
Shiro四大功能:认证登录,授权,会话管理和加密:
- Authentication: Sometimes referred to as 'login', this is the act of proving a user is who they say they are.
- Authorization: The process of access control, i.e. determining 'who' has access to 'what'.
- Session Management: Managing user-specific sessions, even in non-web or EJB applications.
- Cryptography: Keeping data secure using cryptographic algorithms while still being easy to use.
1.3. 架构
2. Shiro使用案例
2.1. 环境搭建
基于Maven项目搭建Shiro环境。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
在resources文件夹中创建shiro.ini文件,用于提供入门案例的账户密码。
[users]
zhangsan=z3
lisi=l4
2.2. 登录认证
public class ShiroRun {
public static void main(String[] args) {
/*
* 1. init SecurityManager
* @Deprecated
* Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
* SecurityManager securityManager = factory.getInstance();
* SecurityUtils.setSecurityManager(securityManager);
*/
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");// shiro.ini在resources的根目录下
securityManager.setRealm(iniRealm);
SecurityUtils.setSecurityManager(securityManager);
// 2. get object of class Subject
Subject subject = SecurityUtils.getSubject();
// 3. create object of class AuthenticationToken, get principle and credentials from data source(web page)
AuthenticationToken token = new UsernamePasswordToken("zhangsan", "z3");
// 4. login,throw exception if login fails
try {
subject.login(token);
System.out.println("Login succeeded.");
} catch (UnknownAccountException e) {
System.out.println("User does not exist.");
} catch (IncorrectCredentialsException e) {
System.out.println("Password error.");
} catch (AuthenticationException e) {
// unexpected condition? error?
}
}
}
2.3. 角色授权
- 编程式: 通过写if/else授权代码完成
if(subject.hasRole("admin")) {
// have anthentication
} else {
// no anthentication
}
- 注解式: 通过在执行的Java方法上放置相应的注解完成,没有权限将抛异常
@RequiresRoles("admin")
public void hello() {
// have anthentication
}
- JSP标签: 在JSP页面通过相应的标签完成
<shiro:hasRole name="admin">
<!-- have anthentication -->
<shiro:hasRole>
修改shiro.ini,添加角色以及权限信息
[users]
zhangsan=z3, role1
lisi=l4, role2
[roles]
role1=user:insert, user:select
// 4. login,throw exception if login fails
try {
subject.login(token);
System.out.println("Login succeeded.");
// 5. judge whether there is a role
boolean hasRole = subject.hasRole("role1");
System.out.println(hasRole);
// 6. judge whether there is a permission
boolean hasPermission = subject.isPermitted("user:insert");
System.out.println(hasPermission);
} catch (AuthenticationException e) {
// unexpected condition? error?
}
2.4. Md5加密
public class ShiroRun {
public static void main(String[] args) {
// clear-text passwords
String password = "gty20021112";
// md5 encryption
Md5Hash md5Hash = new Md5Hash(password);
System.out.println("md5 encryption: " + md5Hash);
/*
* encryption with salt, the salt means that
* append a string to the end of original password then encrypting
*/
Md5Hash md5HashWithSalt = new Md5Hash(password, "xuetian");
System.out.println("md5 encryption with salt: " + md5HashWithSalt);
// multiple encryption
Md5Hash multiMd5HashWithSalt = new Md5Hash(password, "xuetian", 3);
System.out.println("3 times md5 encryption with salt: " + multiMd5HashWithSalt);
}
}
2.5. 自定义登录认证
Shiro默认的登录认证是不带加密的,如果想要实现加密认证需要自定义登录认证,自定义Realm。这里登录账号密码基于ini文件,需要再shiro.ini中添加配置信息。
[main]
md5CredentialsMatcher=org.apache.shiro.authc.credential.Md5CredentialsMatcher
Md5CredentialsMatcher.hashIterations=3
customRealm=com.example.CustomRealm
customRealm.credentialsMatcher=$md5CredentialsMatcher
securityManager.realm=$customRealm
[users]
zhangsan=d954b66f8e663abea8317592a1d4d6aa, role1
lisi=l4, role2
[roles]
role1=user:insert, user:select
重写doGetAuthenticationInfo方法
/**
* 自定义登录认证方法,shiro的login方法底层会调用该类的认证方法进行认证
* 需要配置自定义的realm生效,再ini文件中配置或与Spring整合后进行配置
*/
public class CustomRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. get principle
String principal = (String) token.getPrincipal();
// 2. get credentials
String credentials = (String) token.getCredentials();
// 3. get user information from database
if ("zhangsan".equals(principal)) {
// the password get from database
String password = "d954b66f8e663abea8317592a1d4d6aa";
// 封装校验逻辑对象并返回
return new SimpleAuthenticationInfo(
token.getPrincipal(),// 登录输入的密码(未加密)
password,// 数据库中查询的密码(加密)
ByteSource.Util.bytes("xuetian"),// 盐的名称
token.getPrincipal().toString()// realm的名称(一般用登录用户名)
);
}
return null;
}
}
3. Spring整合Shiro框架
在pom.xml中导入以下依赖。
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.10.0</version>
</dependency>
CustomRealm.java
import com.gty.entity.Member;
import com.gty.service.MemberService;
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.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CustomRealm extends AuthorizingRealm {
@Autowired
private MemberService memberService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//TODO
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. get principle
String principal = (String) token.getPrincipal();
// 2. get user information from database
Member member = memberService.loadMemberByUsername(principal);
if (member != null) {
// 3. the password get from database
String password = member.getPassword();
return new SimpleAuthenticationInfo(
token.getPrincipal(),
password,
ByteSource.Util.bytes("xuetian"),// the salt's name
"realmName"// the name of realm
);
}
return null;
}
}
spring_shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描组件(CustomRealm)-->
<context:component-scan base-package="com.gty.security"/>
<!-- 配置自定义realm -->
<bean id="customRealm" class="com.gty.security.CustomRealm">
<property name="credentialsMatcher">
<!-- 配置加密器 -->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="1024"/>
</bean>
</property>
</bean>
<!-- 配置SecurityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
</bean>
<!-- 配置Shiro的Web过滤器, bean id必须与web.xml中配置的名称相同 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 配置SecurityManager安全管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 如果没有认证,则跳转到登录页面 -->
<property name="loginUrl" value="/login"/>
<!-- 如果没有权限,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/unauthorized"/>
<!--登陆成功-->
<property name="successUrl" value="/index"/>
<!-- Shiro过滤器链 -->
<property name="filterChainDefinitions">
<value>
/member/login = anon
/member/logout = logout
/** = authc
/js/** anon
/images/** anon
/styles/** anon
</value>
</property>
</bean>
</beans>
在web.xml中添加
<!-- shiro过滤器,DelegatingFilterProxy作用是自动到spring容器查找名字为shiroFilter(filter-name)的bean并把所有Filter的操作委托给它,ShiroFilter需要配置到spring容器 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 设置true由servlet容器控制filter的生命周期 -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证</h1>
<form th:action="@{/member/login}" method="get">
<div><input type="text" name="username" value=""/></div>
<div><input type="password" name="password" value=""/></div>
<div><button type="submit">登录</button></div>
</form>
</body>
</html>
标签:Shiro,import,apache,org,password,shiro
From: https://www.cnblogs.com/hualuosakura/p/16997529.html