Spring集成Shiro
文章目录
Shiro入门
关于Shiro的介绍网上很多资料,我这里就不再强调了
权限分类
权限一般分为登录权限和操作授权两部分
Shiro的四个组成部分:用户-角色-权限-资源
使用Shiro
创建好项目,然后在pom.xml文件中加入依赖
<!-- shiro核心jar包 -->
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
准备ini文件
文件中有角色权限等
# -----------------------------------------------------------------------------
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[users]
root = 123456, admin
guest = guest, guest
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
guest = department:*
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
测试基本功能
package com.ifueen.shiro;
import org.apache.shiro.mgt.SecurityManager;
public class HelloShiro {
@Test
public void testShiroHello(){
//拿到核心对象并且读取ini文件
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
//拿到上下文对象
SecurityUtils.setSecurityManager(securityManager);
//拿到当前的用户
Subject currentUser = SecurityUtils.getSubject();
//判断是否登录
System.out.println("是否登录:"+currentUser.isAuthenticated());
//获取到登录令牌
UsernamePasswordToken token = new UsernamePasswordToken("guest", "guest");
//登录
try {
currentUser.login(token);
} catch (AuthenticationException e) {
System.out.println("用户名错误,建议看脑科");
}
System.out.println("是否登录:"+currentUser.isAuthenticated());
System.out.println("是否是admin角色:"+currentUser.hasRole("admin"));
System.out.println("是否能够操作employee:"+currentUser.isPermitted("employee:*"));
//退出
currentUser.logout();
System.out.println("是否登录:"+currentUser.isAuthenticated());
}
}
自定义Realm
package com.ifueen.shiro;
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.apache.shiro.util.ByteSource;
import java.util.HashSet;
import java.util.Set;
/**
* 自定义一个Realm
*/
public class MyRealm extends AuthorizingRealm {
//授权功能
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//从数据库中获取到角色并且方法授权对象中
Set<String> roles = getRoles();
authorizationInfo.setRoles(roles);
//从数据库中获取到权限并且方法授权对象中
Set<String> perms = getPerms();
authorizationInfo.setStringPermissions(perms);
return authorizationInfo;
}
/**
* 假设这里是角色管理
* @return
*/
public Set<String> getRoles(){
Set<String> set = new HashSet<String>();
set.add("admin");
set.add("HR");
return set;
}
/**
* 假设这里是权限管理
* @return
*/
public Set<String> getPerms(){
Set<String> set = new HashSet<String>();
set.add("*");
return set;
}
//身份认证功能
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//拿到令牌
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//通过令牌拿到用户名
String username = token.getUsername();
//通过用户名查找到密码
String password = getUser(username);
//如果密码不存在(表示没有通过用户名查找到用户,用户不存在的情况)
if (password==null){
return null;
}
/**
* 返回的SimpleAuthenticationInfo对象
* 第一个参数表示主体:随便填写
* 第二个参数表示密码:填写数据库中的密码
* 第三个参数:盐值
* 第四个参数表示这个Realm的名称:自定义名字
*/
//对于加密后的密码,这里不需要进行加密,shiro会自动判断密码是否正确,这里只需要写正常功能即可
//拿到盐值对象
ByteSource salt = ByteSource.Util.bytes("ifueen");
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, salt,"myRealm");
return authenticationInfo;
}
/**
* 这里模拟一个返回密码的方法
* @param username
* @return
*/
public String getUser(String username){
if ("admin".equals(username)){
return "06a49f27623237c3ca826ca617661285";
}else if ("come".equals(username)){
return "45565";
}
return null;
}
}
测试自定义Realm
/**
* 测试MyRealm
*/
public class MyRealmTest {
@Test
public void testMyRealm(){
//创建MyRealm对象
MyRealm myRealm = new MyRealm();
//通过Realm对象拿到SecurityManager对象
DefaultSecurityManager securityManager = new DefaultSecurityManager(myRealm);
//将securityManager设置到上下文中
SecurityUtils.setSecurityManager(securityManager);
//拿到当前用户
Subject subject = SecurityUtils.getSubject();
//设置Hash算法
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("MD5");
matcher.setHashIterations(10);
//把匹配其交给shiro
myRealm.setCredentialsMatcher(matcher);
System.out.println("是否登录:"+subject.isAuthenticated());
//没有登录就让它登录
if (!subject.isAuthenticated()){
//拿到令牌
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try {
subject.login(token);
} catch (UnknownAccountException e) {
System.out.println("账户不存在啊大哥");
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误,建议自杀");
} catch (AuthenticationException e) {
System.out.println("发生了未知错误");
}
}
System.out.println("用户是否登录:"+subject.isAuthenticated());
System.out.println("用户是否为admin角色:"+subject.hasRole("admin"));
System.out.println("用户是否能够操作employee:"+subject.isPermitted("employee:save"));
}
/**
* 测试MD5加密
*
*/
@Test
public void testHash(){
/**
* 参数一:加密的算法
* 参数二:原密码
* 参数三:盐值(就是随便加点东西在后边)
* 参数四:加密次数
*/
SimpleHash hash = new SimpleHash("MD5", "123456", "ifueen", 10);
System.out.println(hash.toString());
}
}
Spring集成shiro
导入shiro的支持包
在pom.xml中加入
<!-- shiro(权限框架)的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
web.xml中配置过滤器
<!-- shiro的过滤器(虚假的过滤器)
它不会只负责拦截,之后不做任何操作
Proxy:代理 -> 需要通过名称去找真正的过滤器
-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
准备好自定义的Realm
直接将刚刚创建的自定义Realm拷贝过来
package com.ifueen.shiro;
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.apache.shiro.util.ByteSource;
import java.util.HashSet;
import java.util.Set;
/**
* 自定义一个Realm
*/
public class MyRealm extends AuthorizingRealm {
//授权功能
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//从数据库中获取到角色并且方法授权对象中
Set<String> roles = getRoles();
authorizationInfo.setRoles(roles);
//从数据库中获取到权限并且方法授权对象中
Set<String> perms = getPerms();
authorizationInfo.setStringPermissions(perms);
return authorizationInfo;
}
/**
* 假设这里是角色管理
* @return
*/
public Set<String> getRoles(){
Set<String> set = new HashSet<String>();
set.add("admin");
set.add("HR");
return set;
}
/**
* 假设这里是权限管理
* @return
*/
public Set<String> getPerms(){
Set<String> set = new HashSet<String>();
set.add("*");
return set;
}
//身份认证功能
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//拿到令牌
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//通过令牌拿到用户名
String username = token.getUsername();
//通过用户名查找到密码
String password = getUser(username);
//如果密码不存在(表示没有通过用户名查找到用户,用户不存在的情况)
if (password==null){
return null;
}
/**
* 返回的SimpleAuthenticationInfo对象
* 第一个参数表示主体:随便填写
* 第二个参数表示密码:填写数据库中的密码
* 第三个参数:盐值
* 第四个参数表示这个Realm的名称:自定义名字
*/
//对于加密后的密码,这里不需要进行加密,shiro会自动判断密码是否正确,这里只需要写正常功能即可
//拿到盐值对象
ByteSource salt = ByteSource.Util.bytes("ifueen");
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, salt,"myRealm");
return authenticationInfo;
}
/**
* 这里模拟一个返回密码的方法
* @param username
* @return
*/
public String getUser(String username){
if ("admin".equals(username)){
return "06a49f27623237c3ca826ca617661285";
}else if ("come".equals(username)){
return "45565";
}
return null;
}
}
准备一个存放权限的工厂
注意:修改了这里的数据后热启动无效,需要重新部署
public class ShiroFilterMapFactory {
public Map<String,String> createMap(){
Map<String,String> map = new LinkedHashMap<>();
//anon:需要放行的路径
map.put("/login","anon");
//perms:权限拦截
map.put("/s/permission.jsp","perms[employee:index]");
//authc:拦截
map.put("/**","authc");
return map;
}
}
配置SpringContext-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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- shiro的核心文件(权限管理器) -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="jpaRealm"/>
</bean>
<!-- 配置自己写的JpaRealm -->
<bean id="jpaRealm" class="com.ifueen.aishell.web.shiro.JpaRealm">
<property name="name" value="jpaRealm"/>
<property name="credentialsMatcher">
<!-- 配置Hash密码匹配器-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 设置加密方式 -->
<property name="hashAlgorithmName" value="MD5"/>
<!-- 设置迭代次数-->
<property name="hashIterations" value="10" />
</bean>
</property>
</bean>
<!-- 支持注解权限 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!--
真正的过滤器
名称必须和web.xml里面的过滤器名称一样
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 权限管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 登录之前的页面 -->
<property name="loginUrl" value="/login"/>
<!-- 登录成功后访问的页面(一般用不到)-->
<property name="successUrl" value="/main"/>
<!-- 没有权限时跳转的页面 -->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<!--
<property name="filterChainDefinitions">
<value>
/login = anon
/s/permission.jsp = perms[user:index]
/** = authc
</value>
</property>
-->
<!-- 配置权限 -->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
</bean>
<bean name="filterChainDefinitionMap" factory-bean="shiroFilterMapFactory" factory-method="createMap"/>
<bean name="shiroFilterMapFactory" class="com.ifueen.aishell.web.shiro.ShiroFilterMapFactory"/>
</beans>