SpringBoot整合Shiro框架实现加密、登录、授权

35 篇文章 21 订阅
4 篇文章 1 订阅

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&amp
    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

要在Spring Boot的定时任务中加锁,你可以使用`@EnableSchedulerLock`注解。这个注解的作用是启用定时任务锁,并设置锁的保留时间。默认情况下,锁的保留时间是30秒。 在启动类上加上`@EnableSchedulerLock`注解可以启用自带的定时任务,并且设置默认的锁保留时间。以下是一个示例: ``` @SpringBootApplication @EnableScheduling @EnableSchedulerLock(defaultLockAtMostFor = "PT30S") public class CmccVoicesApiApplication { public static void main(String[] args) { SpringApplication.run(CmccVoicesApiApplication.class, args); } } ``` 在加锁的过程中,会使用一个名为`name`的主键字段来标识每个定时任务的名字,在加锁时会记录锁的开始时间和结束时间。这些信息可以帮助其他节点判断锁的状态。 如果你想在Spring Boot中使用分布式定时任务锁,需要添加相关的依赖。可以使用`shedlock-spring`和`shedlock-provider-redis-spring`依赖来实现基于Redis的分布式任务锁。 以下是相关的依赖配置: ```xml <!-- 分布式定时任务锁 --> <!-- https://mvnrepository.com/artifact/net.javacrumbs.shedlock/shedlock-spring --> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-spring</artifactId> <version>4.0.4</version> </dependency> <!-- 使用redis做分布式任务 --> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-provider-redis-spring</artifactId> <version>2.5.0</version> </dependency> ``` 这样配置后,你就可以在Spring Boot的定时任务中加锁,并实现分布式任务锁的功能了。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pan_junbiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值