Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
下面将使用SpringBoot整合Shiro框架实现加密、登录、授权。
1、创建数据表
使用MySQL数据库,创建实例所需的数据表,用户信息表、角色信息表、权限信息表等。
1.1 数据表结构
1.2 数据表脚本
(1)创建数据表脚本。
-- 创建数据表:user_info(用户信息表)
DROP TABLE IF EXISTS user_info;
CREATE TABLE IF NOT EXISTS user_info
(
user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID(主键、自增)',
user_name VARCHAR(50) UNIQUE NOT NULL COMMENT '用户姓名',
PASSWORD VARCHAR(50) NOT NULL COMMENT '登录密码',
salt VARCHAR(50) NOT NULL NOT NULL COMMENT '盐',
state CHAR(2) DEFAULT '0' COMMENT '状态(0:禁用;1:锁定;2:启用)',
blog_url VARCHAR(50) NOT NULL COMMENT '博客地址',
blog_remark VARCHAR(50) COMMENT '博客信息'
) COMMENT = '用户信息表';
-- 创建数据表:permission_info(权限信息表)
DROP TABLE IF EXISTS permission_info;
CREATE TABLE IF NOT EXISTS permission_info
(
permission_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '权限ID(主键、自增)',
permission_code VARCHAR(50) UNIQUE NOT NULL COMMENT '权限编号',
permission_name VARCHAR(50) NOT NULL COMMENT '权限名称'
) COMMENT = '权限信息表';
-- 创建数据表:role_info(角色信息表)
DROP TABLE IF EXISTS role_info;
CREATE TABLE IF NOT EXISTS role_info
(
role_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '角色ID(主键、自增)',
role_code VARCHAR(50) UNIQUE NOT NULL COMMENT '角色编号',
role_name VARCHAR(50) NOT NULL COMMENT '角色名称'
) COMMENT = '角色信息表';
-- 创建数据表:role_permission_mapping(角色与权限映射表)
DROP TABLE IF EXISTS role_permission_mapping;
CREATE TABLE IF NOT EXISTS role_permission_mapping
(
mapping_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '映射ID(主键、自增)',
role_id INT NOT NULL COMMENT '角色ID',
permission_id INT NOT NULL COMMENT '权限ID'
) COMMENT = '角色与权限映射表';
-- 创建数据表:user_role_mapping(用户与角色映射表)
DROP TABLE IF EXISTS user_role_mapping;
CREATE TABLE IF NOT EXISTS user_role_mapping
(
mapping_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '映射ID(主键、自增)',
user_id INT NOT NULL COMMENT '用户ID',
role_id INT NOT NULL COMMENT '角色ID'
) COMMENT = '用户与角色映射表';
(2)初始化数据脚本。
-- 初始化:user_info(用户信息表)
TRUNCATE TABLE user_info;
INSERT INTO user_info(user_name,PASSWORD,salt,state,blog_url,blog_remark)
VALUE('pan_junbiao的博客','ab2d90c1307e8a2733d8332710b0ca16','ca4f8a8f17f0e2f80698ba3005b2df15'
,2,'https://blog.csdn.net/pan_junbiao','您好,欢迎访问 pan_junbiao的博客');
-- 初始化:permission_info(权限信息表)
TRUNCATE TABLE permission_info;
INSERT INTO permission_info(permission_code,permission_name) VALUES('user:list','用户列表');
INSERT INTO permission_info(permission_code,permission_name) VALUES('user:detail','用户详情');
INSERT INTO permission_info(permission_code,permission_name) VALUES('user:add','新增用户');
INSERT INTO permission_info(permission_code,permission_name) VALUES('user:edit','编辑用户');
INSERT INTO permission_info(permission_code,permission_name) VALUES('user:delete','删除用户');
INSERT INTO permission_info(permission_code,permission_name) VALUES('user:summarize','用户统计');
INSERT INTO permission_info(permission_code,permission_name) VALUES('system:info','系统信息');
-- 初始化:role_info(角色信息表)
TRUNCATE TABLE role_info;
INSERT INTO role_info(role_code,role_name) VALUES('2001','用户管理员');
INSERT INTO role_info(role_code,role_name) VALUES('2002','数据分析员');
INSERT INTO role_info(role_code,role_name) VALUES('2003','系统管理员');
-- 初始化:role_permission_mapping(角色与权限映射表)
TRUNCATE TABLE role_permission_mapping;
INSERT INTO role_permission_mapping(role_id,permission_id) VALUES(1,1);
INSERT INTO role_permission_mapping(role_id,permission_id) VALUES(1,2);
INSERT INTO role_permission_mapping(role_id,permission_id) VALUES(1,3);
INSERT INTO role_permission_mapping(role_id,permission_id) VALUES(1,4);
INSERT INTO role_permission_mapping(role_id,permission_id) VALUES(1,5);
INSERT INTO role_permission_mapping(role_id,permission_id) VALUES(2,6);
INSERT INTO role_permission_mapping(role_id,permission_id) VALUES(3,7);
-- 初始化:user_role_mapping(用户与角色映射表)
TRUNCATE TABLE user_role_mapping;
INSERT INTO user_role_mapping(user_id,role_id) VALUES(1,1);
INSERT INTO user_role_mapping(user_id,role_id) VALUES(1,3);
2、创建项目
(1)创建SpringBoot项目,项目结构如下图:
(2)添加pom.xml配置信息
在pom.xml配置文件中添加Shiro、JPA、Thymeleaf等依赖。
<!-- Shiro依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
<!-- 引入Thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Spring Data JPA依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL的JDBC数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
(3)配置数据库连接信息
SpringBoot项目使用MySQL等关系型数据库,需要配置连接信息。
将默认的application.properties文件的后缀修改为“.yml”,即配置文件名称为:application.yml,并配置以下MySQL数据库的连接信息:
spring:
#使用Thymeleaf模板引擎
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false #使用Thymeleaf模板引擎,关闭缓存
servlet:
content-type: text/html
# JPA数据库连接信息
datasource:
url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
database: MYSQL
show-sql: true
open-in-view: true
properties:
hibernate:
enable_lazy_load_no_trans: true #使用延时加载时控制Session的生命周期
dialect: org.hibernate.dialect.MySQL5Dialect
ddl-auto: update
2.1 创建实体类(Entity层)
在com.pjb.entity包中,创建UserInfo类(用户信息实体类)。
package com.pjb.entity;
import javax.persistence.*;
import java.util.List;
/**
* 用户信息实体类
* @author pan_junbiao
**/
@Entity
@Table(name = "user_info")
public class UserInfo
{
//用户ID(主键、自增)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private int userId;
//用户姓名
@Column(name = "user_name")
private String userName;
//登录密码
@Column(name = "PASSWORD")
private String password;
//盐
@Column(name = "salt")
private String salt;
//状态(0:禁用;1:锁定;2:启用)
@Column(name = "state")
private int state;
//博客地址
@Column(name = "blog_url")
private String blogUrl;
//博客信息
@Column(name = "blog_remark")
private String blogRemark;
//角色实体对象集合
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_role_mapping",joinColumns = {@JoinColumn(name = "user_id")},inverseJoinColumns = {@JoinColumn(name="role_id")})
private List<RoleInfo> roleList;
//省略getter与setter方法...
}
创建PermissionInfo类(权限信息实体类)。
package com.pjb.entity;
import javax.persistence.*;
/**
* 权限信息实体类
* @author pan_junbiao
**/
@Entity
@Table(name="permission_info")
public class PermissionInfo
{
//权限ID(主键、自增)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "permission_id")
private int permissionId;
//权限编号
@Column(name = "permission_code")
private String permissionCode;
//权限名称
@Column(name = "permission_name")
private String permissionName;
//省略getter与setter方法...
}
创建RoleInfo类(角色信息实体类)。
package com.pjb.entity;
import javax.persistence.*;
import java.util.List;
/**
* 角色信息实体类
* @author pan_junbiao
**/
@Entity
@Table(name="role_info")
public class RoleInfo
{
//角色ID(主键、自增)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private int roleId;
//角色编号
@Column(name = "role_code")
private String roleCode;
//角色名称
@Column(name = "role_name")
private String roleName;
//权限实体对象集合
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "role_permission_mapping",joinColumns = {@JoinColumn(name = "role_id")},inverseJoinColumns = {@JoinColumn(name="permission_id")})
private List<PermissionInfo> permissionInfoList;
//省略getter与setter方法...
}
2.2 数据库访问层(Dao层)
在com.pjb.dao包中,创建UserDao接口(用户数据访问接口),并继承JpaRepository接口。
package com.pjb.dao;
import com.pjb.entity.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
/**
* 用户数据访问接口
* @author pan_junbiao
**/
public interface UserDao extends JpaRepository<UserInfo, Integer>
{
/**
* 根据用户姓名,获取用户信息
*/
@Query("SELECT u FROM UserInfo u WHERE u.userName = :userName")
public UserInfo getStaffByUserName(@Param("userName") String userName);
}
3、SpringBoot整合Shiro框架
(1)在com.pjb.config包中,创建ShiroRealm类(Shiro域)。
package com.pjb.config;
import com.pjb.dao.UserDao;
import com.pjb.entity.PermissionInfo;
import com.pjb.entity.UserInfo;
import com.pjb.entity.RoleInfo;
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 javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
/**
* Shiro域
* @author pan_junbiao
**/
public class ShiroRealm extends AuthorizingRealm
{
@Resource
private UserDao userDao;
@Override
/**
* 权限配置
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
{
//创建Shiro授权对象
SimpleAuthorizationInfo authorization = new SimpleAuthorizationInfo();
//获取用户信息
UserInfo staffInfo = (UserInfo) principals.getPrimaryPrincipal();
//遍历角色与权限
List<RoleInfo> roleInfoList = staffInfo.getRoleList();
if (roleInfoList != null && roleInfoList.size() > 0)
{
roleInfoList.forEach(role ->
{
//添加角色信息
authorization.addRole(role.getRoleCode());
//添加权限信息
List<PermissionInfo> permissionInfoList = role.getPermissionInfoList();
if (permissionInfoList != null && permissionInfoList.size() > 0)
{
List<String> permissions = permissionInfoList.stream().map(PermissionInfo::getPermissionCode).collect(Collectors.toList());
authorization.addStringPermissions(permissions);
}
});
}
return authorization;
}
/**
* 进行身份认证,判断用户名密码是否匹配正确
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
//获取用户的输入的账号
String userName = (String) token.getPrincipal();
//System.out.println("身份:"+userName);
//System.out.println("凭证:"+token.getCredentials());
if (userName == null || userName.length() == 0)
{
return null;
}
//获取用户信息
UserInfo userInfo = userDao.getStaffByUserName(userName);
if (userInfo == null)
{
throw new UnknownAccountException(); //未知账号
}
//判断账号是否被锁定,状态(0:禁用;1:锁定;2:启用)
if(userInfo.getState() == 0)
{
throw new DisabledAccountException(); //帐号禁用
}
if (userInfo.getState() == 1)
{
throw new LockedAccountException(); //帐号锁定
}
//验证
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo, //用户名
userInfo.getPassword(), //密码
ByteSource.Util.bytes(userInfo.getSalt()), //盐
getName() //realm name
);
return authenticationInfo;
}
/*
DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、
UnknownAccountException(错误的帐号)、ExcessiveAttemptsException(登录失败次数过多)、
IncorrectCredentialsException (错误的凭证)、ExpiredCredentialsException(过期的凭证)等
*/
}
(2)创建ShiroConfig类(Shiro配置类)。
package com.pjb.config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
/**
* Shiro配置类
* @author pan_junbiao
**/
@Configuration
public class ShiroConfig
{
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager)
{
//shiroFilter
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不需要权限的资源
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/image/**","anon");
filterChainDefinitionMap.put("/toLoginView", "anon");
filterChainDefinitionMap.put("/login", "anon");
//配置退出过滤器,退出代码Shiro已经替我们实现
//filterChainDefinitionMap.put("/logout", "logout");
//过滤链定义,从上向下顺序执行,/**放在最下边;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 凭证匹配器
* 密码校验交给Shiro的SimpleAuthenticationInfo进行处理
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次数;
return hashedCredentialsMatcher;
}
@Bean
public ShiroRealm myShiroRealm() {
ShiroRealm myShiroRealm = new ShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
@Bean(name = "simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
mappings.setProperty("UnauthorizedException", "403");
r.setExceptionMappings(mappings);
r.setDefaultErrorView("error");
r.setExceptionAttribute("ex"); // 缺省值"exception"
return r;
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
}
4、Shiro的使用
Shiro安全框架使用简单,它可以实现账号密码加密、登录验证、授权等功能。
4.1 加盐加密
【实例】使用Shiro给账号密码加盐、加密。
package com.pjb.User;
import com.pjb.dao.UserDao;
import com.pjb.entity.UserInfo;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 用户信息测试类
* @author pan_junbiao
**/
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserTest
{
@Autowired
private UserDao userDao;
/**
* 新增用户
* 账号密码的加密、加盐
*/
@Test
public void addUser()
{
String originalPassword = "123456"; //原始密码
String hashAlgorithmName = "MD5"; //加密方式
int hashIterations = 2; //加密的次数
//盐
String salt = new SecureRandomNumberGenerator().nextBytes().toHex();
//加密
SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, originalPassword, salt, hashIterations);
String encryptionPassword = simpleHash.toString();
//创建用户信息
UserInfo userInfo = new UserInfo();
userInfo.setUserName("pan_junbiao的博客_02");
userInfo.setPassword(encryptionPassword);
userInfo.setSalt(salt);
userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");
userInfo.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");
userInfo.setState(2);
//执行新增
userDao.save(userInfo);
//打印结果
System.out.println("用户ID:" + userInfo.getUserId());
System.out.println("用户姓名:" + userInfo.getUserName());
System.out.println("原始密码:" + originalPassword);
System.out.println("加密密码:" + userInfo.getPassword());
System.out.println("盐:" + userInfo.getSalt());
System.out.println("博客地址:" + userInfo.getBlogUrl());
System.out.println("博客信息:" + userInfo.getBlogRemark());
}
}
执行结果:
4.2 登录验证
【实例】使用Shiro实现登录、登出,执行结果如下图:
(1)在com.pjb.controller包中,创建LoginController类(登录控制器),实现登录、登出方法。
package com.pjb.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* 登录控制器
* @author pan_junbiao
**/
@Controller
public class LoginController
{
@Autowired
public HttpServletRequest request;
/**
* 登录页面
*/
@RequestMapping("/toLoginView")
public String toLoginView()
{
return "login.html";
}
/**
* 登录操作
*/
@RequestMapping("/login")
public String login(String userName, String password)
{
//参数验证
if (userName == null || userName.length() == 0 || password == null || password.length() == 0)
{
return "login.html";
}
//账号密码令牌
AuthenticationToken token = new UsernamePasswordToken(userName, password);
//获得当前用户到登录对象,现在状态为未认证
Subject subject = SecurityUtils.getSubject();
try
{
//将令牌传到shiro提供的login方法验证,需要自定义realm
subject.login(token);
//没有异常表示验证成功,进入首页
return "redirect:/index";
}
catch (IncorrectCredentialsException ice)
{
request.setAttribute("message", "用户名或密码不正确!");
}
catch (UnknownAccountException uae)
{
request.setAttribute("message", "未知账户!");
}
catch (LockedAccountException lae)
{
request.setAttribute("message", "账户被锁定!");
}
catch (DisabledAccountException dae)
{
request.setAttribute("message", "账户被禁用!");
}
catch (ExcessiveAttemptsException eae)
{
request.setAttribute("message", "用户名或密码错误次数太多!");
}
catch (AuthenticationException ae)
{
request.setAttribute("message", "验证未通过!");
}
catch (Exception e)
{
request.setAttribute("message", "验证未通过!");
}
return "login.html";
}
/**
* 登出操作
*/
@RequestMapping("/logout")
public String logout()
{
//登出清除缓存
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/toLoginView";
}
}
登录验证的异常列表:
异常类型 | 异常说明 |
---|---|
DisabledAccountException | 禁用的帐号 |
LockedAccountException | 锁定的帐号 |
UnknownAccountException | 错误的帐号 |
ExcessiveAttemptsException | 登录失败次数过多 |
IncorrectCredentialsException | 错误的凭证(用户名或密码错误) |
ExpiredCredentialsException | 过期的凭证 |
(2)创建login.html(登录页面)。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
<meta name="author" content="pan_junbiao的博客">
<style>
.txtBox{
padding: 3px;
width: 250px;
font-size: 16px;
}
</style>
</head>
<body>
<div align="center">请输入登录信息
<form name="myForm" method="post" action="/login" onsubmit="SubmitLogin()">
<table>
<tr>
<td>用户姓名:</td>
<td><input type="text" name="userName" value="pan_junbiao的博客" class="txtBox" /></td>
</tr>
<tr>
<td>登录密码:</td>
<td><input type="password" name="password" value="123456" class="txtBox"/></td>
</tr>
<!-- 以下是提交、取消按钮 -->
<tr>
<td>
<input type="submit" value="登录" />
</td>
<td>
<input type="reset" value="取消" />
</td>
</tr>
</table>
<p style="color:red" th:text="${message}"></p>
</form>
</div>
</body>
<script>
//提交登录
function SubmitLogin() {
//判断用户名是否为空
if (!myForm.userName.value) {
alert("请输入用户姓名!");
myForm.userName.focus();
return false;
}
//判断密码是否为空
if (!myForm.password.value) {
alert("请输入登录密码!");
myForm.password.focus();
return false;
}
return true;
}
</script>
</html>
4.3 获取当前登录人信息
Shiro获取当前登录人信息语句如下:
//获取当前登录人
User currentUser = (User) SecurityUtils.getSubject().getPrincipal();
【实例】当登录成功后,进行首页,在首页中显示当前登录人信息,执行结果如下图:
(1)创建IndexController类(首页控制器),获取当前登录人信息,并返回到页面。
package com.pjb.controller;
import com.pjb.entity.UserInfo;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* 首页控制器
* @author pan_junbiao
**/
@Controller
public class IndexController
{
/**
* 首页
*/
@RequestMapping("/index")
public String index(HttpServletRequest request)
{
//获取当前登录人
UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();
request.setAttribute("userInfo",userInfo);
return "index.html";
}
}
(2)创建index.html(首页页面)。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
<meta name="author" content="pan_junbiao的博客">
<style>
table { border-collapse: collapse; margin-bottom: 10px}
table,table tr th, table tr td { border:1px solid #000000; padding: 5px 10px;}
a{margin-right: 10px;}
</style>
</head>
<body>
<div align="center">
<table>
<caption>当前登录人信息</caption>
<tr>
<th>用户ID:</th>
<td th:text="${userInfo.userId}"></td>
</tr>
<tr>
<th>用户姓名:</th>
<td th:text="${userInfo.userName}"></td>
</tr>
<tr>
<th>博客地址:</th>
<td th:text="${userInfo.blogUrl}"></td>
</tr>
<tr>
<th>博客信息:</th>
<td th:text="${userInfo.blogRemark}"></td>
</tr>
<tr>
<th>角色信息:</th>
<td>
<th:block th:each="item:${userInfo.roleList}">
<span th:text="${item.roleName}"></span>
</th:block>
</td>
</tr>
<tr>
<th>注销操作:</th>
<td>
<a href="logout" onclick="return confirm('确认注销吗?');" >注销</a>
</td>
</tr>
<tr>
<th>授权测试:</th>
<td>
<a href="/user/searchUser" >查询用户</a>
<a href="/user/addUser" >新增用户</a>
<a href="/user/editUser" >修改用户</a>
<a href="/user/deleteUser" >删除用户</a>
</td>
</tr>
<tr>
<th>无权限测试:</th>
<td>
<a href="/user/summarizeUser" >用户统计(无权限)</a>
</td>
</tr>
</table>
</div>
</body>
</html>
4.4 授权
Shiro可以使用@RequiresPermissions或者@RequiresRoles注解进行方法或类的授权。
例如:
@RequiresPermissions("user:list")
多个授权时:
@RequiresPermissions({"user:add","user:edit"})
【实例】实现控制器方法的授权,当没有权限时跳转到403页面。
(1)创建UserController(用户信息控制器),编写控制器方法,所有的方法仅测试授权功能。
package com.pjb.controller;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* 用户信息控制器
* 所有方法仅测试授权功能
* @author pan_junbiao
**/
@Controller
@RequestMapping("/user")
public class UserController
{
@Autowired
public HttpServletRequest request;
/**
* 查询用户
*/
@RequiresPermissions("user:list")
@RequestMapping("/searchUser")
public String searchUser()
{
request.setAttribute("message","查询用户");
return "user.html";
}
/**
* 新增用户
*/
@RequiresPermissions("user:add")
@RequestMapping("/addUser")
public String addUser()
{
request.setAttribute("message","新增用户");
return "user.html";
}
/**
* 修改用户
*/
@RequiresPermissions("user:edit")
@RequestMapping("/editUser")
public String editUser()
{
request.setAttribute("message","修改用户");
return "user.html";
}
/**
* 删除用户
*/
@RequiresPermissions("user:delete")
@RequestMapping("/deleteUser")
public String deleteUser()
{
request.setAttribute("message","删除用户");
return "user.html";
}
/**
* 用户统计(无权限)
*/
@RequiresPermissions("user:summarize")
@RequestMapping("/summarizeUser")
public String summarizeUser()
{
request.setAttribute("message","用户统计");
return "user.html";
}
}
(2)创建user.html页面,当登录用户有权限时,跳转到该页面,并显示当前的操作功能。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户操作</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<h2>恭喜您,有操作权限!</h2>
<p>您好,欢迎访问 pan_junbiao的博客</p>
<p>博客地址:https://blog.csdn.net/pan_junbiao</p>
<p th:text="'当前操作:' + ${message}"></p>
</body>
</html>
执行结果:
(3)创建403.html页面,当登录用户有没有权限时,跳转到该页面,并提示用户权限不足。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>403无权限</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<h2>403无权限</h2>
<p>您好,欢迎访问 pan_junbiao的博客</p>
<p>博客地址:https://blog.csdn.net/pan_junbiao</p>
<span style="color:red">哎呀!您的权限不足。</span>
</body>
</html>
执行结果:
源代码下载:https://github.com/kevinpanjunbiao/ShiroProject
原文地址:https://blog.csdn.net/pan_junbiao/article/details/106437248