logo头像

不破不立

Shiro整入SpringMVC简单Demo

在权限框架这块,用了一段时间Shiro,这里记录了一下笔记,主要是将Shiro初步整入了SpringMVC项目,该项目没有实际连接数据库,采用模拟数据进行,主要实现了认证授权功能。

Shiro

以下概念参考来自 https://legacy.gitbook.com/book/waylau/apache-shiro-1-2-x-reference/details

##简介

 Apache Shiro是一个功能强大、灵活的,开源的安全框架。

ShiroFeatures

  Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被 Shiro 框架的开发团队称之为应用安全的四大基石 。

  • Authentication(认证):用户身份识别,通常被称为用户“登录”
  • Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
  • Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
  • Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。

还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持:

  • Web支持:Shiro 提供的 web 支持 api ,可以很轻松的保护 web 应用程序的安全。
  • 缓存:缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。
  • 并发:Apache Shiro 支持多线程应用程序的并发特性。
  • 测试:支持单元测试和集成测试,确保代码和预想的一样安全。
  • “Run As”:这个功能允许用户假设另一个用户的身份(在许可的前提下)。
  • “Remember Me”:跨 session 记录用户的身份,只有在强制需要时才需要登录。

##架构

###高级概述

在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm

ShiroBasicArchitecture

  • Subject:Subject 本质上是当前运行用户特定的’View’(视图),而单词“User”经常暗指一个人,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。 Subject 实例都和(也需要)一个 SecurityManager 绑定,当你和一个Subject 进行交互,这些交互动作被转换成 SecurityManager 下Subject 特定的交互动作。
  • SecurityManager: SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。然而,一旦一个程序配置好了SecurityManager 和它的内部对象,SecurityManager通常独自留下来,程序开发人员几乎花费的所有时间都集中在 Subjet API上。 我们将在以后详细讨论 SecurityManager,但当你和一个 Subject 互动时了解它是很重要的。任何 Subject 的安全操作中 SecurityManager 是幕后真正的举重者,这在上面的图表中可以反映出来。
  • Realms: Reamls 是 Shiro 和你的程序安全数据之间的“桥”或者“连接”,它用来实际和安全相关的数据如用户执行身份认证(登录)的帐号和授权(访问控制)进行交互,Shiro 从一个或多个程序配置的 Realm 中查找这些东西。 Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。SecurityManager 可以配置多个复杂的 Realm,但是至少有一个是需要的。 Shiro 提供开箱即用的 Realms 来连接安全数据源(或叫地址)如 LDAP、JDBC、文件配置如INI和属性文件等,如果已有的Realm不能满足你的需求你也可以开发自己的Realm实现。 和其它内部组件一样,Shiro SecurityManager 管理如何使用 Realms获取 Subject 实例所代表的安全和身份信息。

###详细架构

ShiroArchitecture

  正在与软件交互的一个特定的实体“view”(用户、第三方服务、时钟守护任务等)。

  如同上面提到的,SecurityManager 是 Shiro 的核心,它基本上就是一把“保护伞”用来协调它管理的组件使之平稳地一起工作,它也管理着 Shiro 中每一个程序用户的视图,所以它知道每个用户如何执行安全操作。

  Authenticator 是一个组件,负责执行和反馈用户的认证(登录),如果一个用户尝试登录,Authenticator 就开始执行。Authenticator 知道如何协调一个或多个保存有相关用户/帐号信息的 Realm,从这些 Realm中获取这些数据来验证用户的身份以确保用户确实是其表述的那个人。

  如果配置了多个 Realm,AuthenticationStrategy 将会协调 Realm 确定在一个身份验证成功或失败的条件(例如,如果在一个方面验证成功了但其他失败了,这次尝试是成功的吗?是不是需要所有方面的验证都成功?还是只需要第一个?)

  Authorizer 是负责程序中用户访问控制的组件,它是最终判断一个用户是否允许做某件事的途径,像 Authenticator 一样,Authorizer 也知道如何通过协调多种后台数据源来访问角色和权限信息,Authorizer 利用这些信息来准确判断一个用户是否可以执行给定的动作。

  SessionManager 知道如何创建并管理用户 Session 生命周期而在所有环境中为用户提供一个强有力的 Session 体验。这在安全框架领域是独一无二–Shiro 具备管理在任何环境下管理用户 Session 的能力,即使没有 Web/Servlet 或者 EJB 容器。默认情况下,Shiro 将使用现有的session(如Servlet Container),但如果环境中没有,比如在一个独立的程序或非 web 环境中,它将使用它自己建立的 session 提供相同的作用,sessionDAO 用来使用任何数据源使 session 持久化。

  SessionDAO 代表 SessionManager 执行 Session 持久(CRUD)动作,它允许任何存储的数据挂接到 session 管理基础上。

  CacheManager 为 Shiro 的其他组件提供创建缓存实例和管理缓存生命周期的功能。因为 Shiro 的认证、授权、会话管理支持多种数据源,所以访问数据源时,使用缓存来提高访问效率是上乘的选择。当下主流开源或企业级缓存框架都可以继承到 Shiro 中,来获取更快更高效的用户体验。

  Cryptography 在安全框架中是一个自然的附加产物,Shiro 的 crypto 包包含了易用且易懂的加密方式,Hashes(即digests)和不同的编码实现。该包里所有的类都亦于理解和使用,曾经用过 Java 自身的加密支持的人都知道那是一个具有挑战性的工作,而 Shiro 的加密 API 简化了 java 复杂的工作方式,将加密变得易用。

  如同上面提到的,Realm 是 shiro 和你的应用程序安全数据之间的“桥”或“连接”,当实际要与安全相关的数据进行交互如用户执行身份认证(登录)和授权验证(访问控制)时,shiro 从程序配置的一个或多个Realm 中查找这些数据,你需要配置多少个 Realm 便可配置多少个 Realm(通常一个数据源一个),shiro 将会在认证和授权中协调它们。

##Shiro实例Web项目

注意:代码贴的顺序可能有些小乱,但是基本上没什么大问题,可以先将实例大致看完,然后进行配置,或者直接前往我的GitHub获取。

地址:https://github.com/panhainan/spring-mvc-shiro-demo

###1. 搭建Spring MVC项目

快速搭建Spring MVC Web项目

###2. 引入Shiro的Jar包

在pom.xml文件中加入以下依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- Spring 整合Shiro需要的依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>

3. web.xml中配置Shiro Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- Shiro Filter is defined in the spring application context: -->
<!--
1. 配置 Shiro 的 shiroFilter.
2. DelegatingFilterProxy 实际上是 Filter 的一个代理对象. 默认情况下, Spring 会到 IOC 容器中查找和
<filter-name> 对应的 filter bean. 也可以通过 targetBeanName 的初始化参数来配置 filter bean 的 id.
--> <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>

4. 配置Shiro相关配置

可以直接在applicationContext.xml文件中添加shiro的配置,这里为方便管理配置,单独将shiro配置作为一个xml文件保存,命名spring-shiro.xml,并在web.xml中引入。

首先修改web.xml中的配置文件扫描,增加,classpath:spring-shiro.xml

1
2
3
4
5
<!--配置加载Spring容器配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml,classpath:spring-shiro.xml</param-value>
</context-param>

然后新建spring-shiro.xml,在其中加入以下代码,相关配置说明见其中注释说明,

核心配置为:

  • SecurityManager
  • Realm(进行)
  • LifecycleBeanPostProcessor
  • ShiroFilter(对应web.xml中的过滤器的过滤条件配置)
  • CacheManager(可选)
  • Annotation(开启注解,可选)
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
<?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.xsd">
<!--
配置 SecurityManager.
-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<property name="realm" ref="shiroRealm"/>
</bean>
<!--
配置 CacheManager.
需要加入 ehcache 的 jar 包及配置文件.
-->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
</bean>

<!--
配置 Realm
直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
-->
<bean id="shiroRealm" class="site.sixteen.demo.shiro.ShiroRealm">
<!-- 配置校验时是否对数据加密,加密算法有MD5,SHA1等 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"></property>
<property name="hashIterations" value="1"></property>
</bean>
</property>
</bean>

<!--
配置 LifecycleBeanPostProcessor. 可以自动的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法.
-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!--
启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 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>

<!--
配置 ShiroFilter.
id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean.
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="successUrl" value="/"/>
<property name="unauthorizedUrl" value="/unauthorized"/>
<!--
配置哪些页面需要受保护.
以及访问这些页面需要的权限.
1). anon 可以被匿名访问
2). authc 必须认证(即登录)后才可能访问的页面.
3). logout 登出.
4). roles 角色过滤器
-->
<property name="filterChainDefinitions">
<value>
/login = anon
/logout = logout
/user/** = roles[user]
/manage/** = roles[admin]
# everything else requires authentication:
/** = authc
</value>
</property>

</bean>
</beans>

5. 继承Realm类实现认证授权

下面代码用到了lombok.jar,可以省去写log实例,getter,setter方法等的编写。

新建一个ShiroRealm类,该类继承自AuthorizingRealm,实现它的认证和授权方法,代码如下,

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
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

@Slf4j
public class ShiroRealm extends AuthorizingRealm {

/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}

6. 实现认证Authentication操作

认证操作的步骤基本如下:

  1. 把 AuthenticationToken 转换为 UsernamePasswordToken
  2. 从 UsernamePasswordToken 中来获取 username
  3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
  4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
  5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.(可选)
  6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo

这里实现认证doGetAuthenticationInfo方法,代码如下

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
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1. 把 AuthenticationToken 转换为 UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;

//2. 从 UsernamePasswordToken 中来获取 username
String username = upToken.getUsername();

//3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
log.info("从数据库中获取 username: {} 所对应的用户信息.", username);
User dbUser = UserDAO.fetchDataFromDB(username);
log.info("数据库中 {} 所对应的用户信息为:{} ", username, dbUser);

//4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
if (dbUser == null) {
throw new UnknownAccountException("用户不存在!");
}

//5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.
if (dbUser.getLocked()) {
throw new LockedAccountException("用户被锁定!");
}

//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
//以下信息是从数据库中获取的.
//1). principal: 认证的实体信息. 可以是 username(通常), 也可以是数据表对应的用户的实体类对象.
Object principal = dbUser.getUsername();
//2). credentials: 密码.
Object credentials = dbUser.getPassword();

//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
String realmName = getName();
//4). 盐值. 这里以用户名为盐值
ByteSource credentialsSalt = ByteSource.Util.bytes(username);

return new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
}

7. 实现登录认证的其他业务类

User.java代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package site.sixteen.demo.entity;

import lombok.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class User {

private Integer id;
private String username;
private String password;
private Boolean locked;

private String[] roles;

}

数据库数据获取模拟类,UserDAO.java:

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
package site.sixteen.demo.dao;

import site.sixteen.demo.entity.User;

/**
* @author panhainan@yeah.net(@link http://sixteen.site)
**/
public class UserDAO {
/**
* 模拟从数据库取数据
*
* @param username
* @return
*/
public static User fetchDataFromDB(String username) {
//这里假定数据库中有一下数据:
// 1,"test","47ec2dd791e31e2ef2076caf64ed9b3d"(原串"123456"),false,roles{"user"}
// 2,"admin","a66abb5684c45962d887564f08346e8d"(原串"123456"),false,roles{"admin","user"}
// 3,"tourist","33d2ebfc6787cdb030d04dc940f8f4ed"(原串"123456"),true,roles{"tourist"}
User dbUser = null;
switch (username) {
case "test":
dbUser = new User(1, "test", "47ec2dd791e31e2ef2076caf64ed9b3d", false, new String[]{"user"});
break;
case "admin":
dbUser = new User(2, "admin", "a66abb5684c45962d887564f08346e8d", false, new String[]{"admin","user"});
break;
case "tourist":
dbUser = new User(3, "tourist", "33d2ebfc6787cdb030d04dc940f8f4ed", true, new String[]{"tourist"});
break;
default:
break;
}
return dbUser;
}
}

枚举类AlgorithmEnum.java,代码如下:

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
public enum AlgorithmEnum {
MD5("MD5", "MD5加密算法"),
SHA1("SHA1", "SHA1加密算法");
private String value;
private String info;

AlgorithmEnum(String value, String info) {
this.value = value;
this.info = info;
}
/**
* 值
* @return
*/
public String value() {
return this.value;
}

/**
* 值说明
* @return
*/
public String info() {
return this.info();
}
}

CryptUtils.java代码,改工具类主要用于在配置好shiro后,对于测试数据进行加密以便于在shiroRealm进行密码匹配后能够正常执行,同时后期需要做的用户注册后用户密码加密存储在数据库中也是一个重要的步骤。

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
package site.sixteen.demo.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import site.sixteen.demo.enums.AlgorithmEnum;

@Slf4j
public class CryptUtils {

public static String encrypt(String saltStr, String toBeEncryptStr, String hashAlgorithmName, int hashIterations) {
Object credentials = toBeEncryptStr;
Object salt = ByteSource.Util.bytes(saltStr);
Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
return String.valueOf(result);
}

public static void main(String[] args) {
String saltStr = "tourist";
String toBeEncryptStr = "123456";
int hashIterations = 1;
log.info("{}:{}", saltStr, toBeEncryptStr);
log.info("{}", CryptUtils.encrypt(saltStr, toBeEncryptStr, AlgorithmEnum.MD5.value(), hashIterations));

}
}

UserLoginException.java用户登录异常处理类

1
2
3
4
5
6
7
8
9
10
11
12
import lombok.Getter;

@Getter
public class UserLoginException extends Exception {

private String message;

public UserLoginException(String message) {
this.message = message;
}

}

创建一个全局异常处理类AppExceptionHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
* @author panhainan@yeah.net(@link http://sixteen.site)
**/
@Slf4j
@ControllerAdvice
public class AppExceptionHandler {

@ExceptionHandler(UserLoginException.class)
public String handleUserLoginException(Exception e, RedirectAttributes redirectAttributes) {
log.info("异常:{}",e.getMessage());
redirectAttributes.addFlashAttribute("errorMsg", e.getMessage());
return "redirect:/login";
}
}

配置全局异常处理器需要在applicationContext.xml增加包扫描处理:

1
<context:component-scan base-package="site.sixteen.demo.exception"/>

UserController.java代码:

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
package site.sixteen.demo.web;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import site.sixteen.demo.entity.User;
import site.sixteen.demo.exception.UserLoginException;

@Slf4j
@Controller
public class UserController {
@GetMapping({"/","/index"})
public String index(){
return "index";
}
@GetMapping("/unauthorized")
public String unauthorized(){
return "unauthorized";
}
@GetMapping("/login")
public String login() {
return "login";
}

@PostMapping("/login")
public String login(User user) throws UserLoginException {
Subject currentSubject = SecurityUtils.getSubject();
if (!currentSubject.isAuthenticated()) {
UsernamePasswordToken upToken = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
// 执行登录.
currentSubject.login(upToken);
} catch (UnknownAccountException e) {
// 若没有指定的账户, 则 shiro 将会抛出 UnknownAccountException 异常.
//一般建议提示用户名密码错误
throw new UserLoginException("用户不存在!");
} catch (IncorrectCredentialsException e) {
// 若账户存在, 但密码不匹配, 则 shiro 会抛出 IncorrectCredentialsException 异常。
//一般建议提示用户名密码错误
throw new UserLoginException("用户密码错误!");
} catch (LockedAccountException e) {
// 用户被锁定的异常 LockedAccountException
throw new UserLoginException("用户已被锁定!");
} catch (AuthenticationException e) {
// 所有认证时异常的父类.
throw new UserLoginException(e.getLocalizedMessage());
}

}
return "redirect:/index";
}
}

login.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="/login" method="post">
Username:<input type="text" name="username"><br><br>
Password:<input type="password" name="password"><br><br>
<input type="submit" value="登录">
${errorMsg}
</form>
</body>
</html>

到此一个简单的登录认证已经实现。效果如下:

接下来实现授权代码的编写。

###8. 实现授权Authorization操作

授权操作主要是实现doGetAuthorizationInfo方法,主要有一下步骤:

  1. 从PrincipalCollection中来获取登录用户的信息
  2. 利用登录的用户信息获取当前用户的角色或权限(可能需要查询数据库)
  3. 创建SimpleAuthorizationInfo,并设置roles属性
  4. 返回SimpleAuthorization对象

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
log.info("授权中:{}", principals.getPrimaryPrincipal());
// 1. 从PrincipalCollection中来获取登录用户的信息
String username = String.valueOf(principals.getPrimaryPrincipal());
// 2. 利用登录的用户信息获取当前用户的角色或权限(可能需要查询数据库)
User DbUser = UserDAO.fetchDataFromDB(username);
// 3. 创建SimpleAuthorizationInfo,并设置roles属性
Set<String> roles = new HashSet<>();
for (String roleStr : DbUser.getRoles()) {
roles.add(roleStr);
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
// 4. 返回SimpleAuthorization对象
return simpleAuthorizationInfo;
}

9. 实现授权的相关类及jsp页面shiro标签设置

index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>首页</title>
</head>
<body>
欢迎您,
<shiro:user>
<shiro:principal/>
</shiro:user>

<ul>
<shiro:hasRole name="user">
<li><a href="/user">用户信息界面</a></li>
</shiro:hasRole>
<shiro:hasRole name="admin">
<li><a href="/manage">管理界面</a></li>
</shiro:hasRole>
<li><a style="color: red" href="/logout">退出</a></li>
</ul>
</body>
</html>

user.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>user界面</title>
</head>
<body>
${user}
</body>
</html>

manage.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>管理页面</title>
</head>
<body>
您好,管理员!
</body>
</html>

unauthorized.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>失败...</title>
</head>
<body>
没有访问权限!
</body>
</html>

结果展示

1534089713356

1534089759301

1534089788526

1534089801997

1534089813786

再次重复代码地址:https://github.com/panhainan/spring-mvc-shiro-demo

因为目前留言插件审核问题无法使用,有疑问的可以联系我的QQ 1016593477或者我的邮箱在我的网站介绍里面可以找到。谢谢!

上一篇

评论系统未开启,无法评论!

如果有好的建议或疑问等可以发送邮件至:panhainan@yeah.net,或者添加QQ:1016593477,将你的建议或者疑问告诉作者,作者会对你的建议进行处理并补充到文章的尾部,谢谢大家的谅解!