Spring集成Shiro

Spring集成Shiro

[TOC]

Shiro入门

关于Shiro的介绍网上很多资料,我这里就不再强调了

权限分类

权限一般分为登录权限和操作授权两部分

Shiro的四个组成部分:用户-角色-权限-资源

使用Shiro

创建好项目,然后在pom.xml文件中加入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 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文件

文件中有角色权限等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -----------------------------------------------------------------------------
# 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

测试基本功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* 测试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中加入

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 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中配置过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 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拷贝过来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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;
}

}

准备一个存放权限的工厂

注意:修改了这里的数据后热启动无效,需要重新部署

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?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>
❤赏点钱让我买杯快乐水8❤