SpringBoot整合Shiro

引入依赖

<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring-boot-web-starter</artifactId>
   <version>1.9.0</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置文件

配置文件 yml或properties,添加基础配置
shiro:
 loginUrl: /myController/login

或者

shiro.loginUrl=/myController/login

登录认证

访问数据库获取用户信息,实现登录认证

后端接口服务实现

创建库表

CREATE DATABASE IF NOT EXISTS `shirodb` CHARACTER SET utf8mb4;
USE `shirodb`;
CREATE TABLE `user` (
 `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
 `name` VARCHAR(30) DEFAULT NULL COMMENT '用户名',
 `pwd` VARCHAR(50) DEFAULT NULL COMMENT '密码',
 `rid` BIGINT(20) DEFAULT NULL COMMENT '角色编号',
 PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';

创建实体

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private Integer rid;
}

创建 mapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.model.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends BaseMapper<User> {
}

创建service接口

import com.example.demo.model.User;

public interface UserService {
 //用户登录
 User getUserInfoByName(String name);
}

创建实现类

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserMapper userMapper;
  @Override
  public User getUserInfoByName(String name) {
     QueryWrapper<User> wrapper = new QueryWrapper<>();
     wrapper.eq("name",name);
     User user = userMapper.selectOne(wrapper);
     return user;
  }
}

自定义realm

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyRealm extends AuthorizingRealm {
     @Autowired
     private UserService userService;
     //自定义授权方法
     @Override
     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
         return null;
     }
     //自定义登录认证方法
     @Override
     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
          //1 获取用户身份信息
          String name = token.getPrincipal().toString();
          //2 调用业务层获取用户信息(数据库中)
          User user = userService.getUserInfoByName(name);
          //3 判断并将数据完成封装
          if(user!=null){
          AuthenticationInfo info = new SimpleAuthenticationInfo(
          token.getPrincipal(),
          user.getPwd(),
          ByteSource.Util.bytes("salt"),
          token.getPrincipal().toString()
          );
          return info;
          }
          return null;
     }
}

编写配置类

import com.example.demo.component.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
      @Autowired
      private MyRealm myRealm;
     
      //配置 SecurityManager
      @Bean
      public DefaultWebSecurityManager defaultWebSecurityManager() {
          //1 创建 defaultWebSecurityManager 对象
          DefaultWebSecurityManager defaultWebSecurityManager = new
                  DefaultWebSecurityManager();
          //2 创建加密对象,并设置相关属性
          HashedCredentialsMatcher matcher = new
                  HashedCredentialsMatcher();
          //2.1 采用 md5 加密
          matcher.setHashAlgorithmName("md5");
          //2.2 迭代加密次数
          matcher.setHashIterations(3);
          //3 将加密对象存储到 myRealm 中
          myRealm.setCredentialsMatcher(matcher);
          //4 将 myRealm 存入 defaultWebSecurityManager 对象
          defaultWebSecurityManager.setRealm(myRealm);
          //5 返回
          return defaultWebSecurityManager;
      }
     
      //配置 Shiro 内置过滤器拦截范围
      @Bean
      public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
          DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
          //设置不认证可以访问的资源
          definition.addPathDefinition("/myController/userLogin", "anon");
          definition.addPathDefinition("/login", "anon");
          //设置需要进行登录认证的拦截范围
          definition.addPathDefinition("/**", "authc");
          return definition;
      }
}

实现controller

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("myController")
public class MyController {
      @GetMapping("userLogin")
      @ResponseBody
      public String userLogin(String name,String pwd){
           //1 获取 Subject 对象
           Subject subject = SecurityUtils.getSubject();
           //2 封装请求数据到 token 对象中
           AuthenticationToken token = new UsernamePasswordToken(name,pwd);
           //3 调用 login 方法进行登录认证
           try {
              subject.login(token);
              return "登录成功";
           } catch (AuthenticationException e) {
              e.printStackTrace();
              System.out.println("登录失败");
              return "登录失败";
           }
      }
}

测试

确认数据库密码是加盐 3 次加密密码

启动服务通过浏览器访问http://localhost:8080/myController/userLogin?name=张三&pwd=z3

实现前端页面

添加 login 页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Shiro 登录认证</h1>
<br>
<form action="/myController/userLogin">
    <div>用户名:<input type="text" name="name" value=""></div>
    <div>密码:<input type="password" name="pwd" value=""></div>
    <div><input type="submit" value="登录"></div>
</form>
</body>
</html>

添加 main 页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<h1>Shiro 登录认证后主页面</h1>
<br>
登录用户为:<span th:text="${session.user}"></span>
</body>

添加 controller 方法

//跳转登录页面
    @GetMapping("login")
    public String login(){
        return "login";
    }
    //登录认证
    @GetMapping("userLogin")
    public String userLogin(String name, String pwd, HttpSession session){
        //1 获取 Subject 对象
        Subject subject = SecurityUtils.getSubject();
        //2 封装请求数据到 token 对象中
        AuthenticationToken token = new UsernamePasswordToken(name,pwd);
        //3 调用 login 方法进行登录认证
        try {
            subject.login(token);
            session.setAttribute("user",token.getPrincipal().toString());
            return "main";
        } catch (AuthenticationException e) {
            e.printStackTrace();
            System.out.println("登录失败");
            return "登录失败";
        }
    }

修改配置类

测试

启动访问测试 localhost:8080/myController/login

多个 realm 的认证策略设置 

多个realm实现原理

当应用程序配置多个 Realm 时,例如:用户名密码校验、手机号验证码校验等等。Shiro 的 ModularRealmAuthenticator 会使用内部的 AuthenticationStrategy 组件判断认证是成功还是失败。
AuthenticationStrategy 是一个无状态的组件,它在身份验证尝试中被询问 4 次(这4 次交互所需的任何必要的状态将被作为方法参数):
  • 在所有 Realm 被调用之前
  • 在调用 Realm 的 getAuthenticationInfo 方法之前
  • 在调用 Realm 的 getAuthenticationInfo 方法之后
  • 在所有 Realm 被调用之后
认证策略的另外一项工作就是聚合所有 Realm 的结果信息封装至一个AuthenticationInfo 实例中,并将此信息返回,以此作为 Subject 的身份信息。 Shiro 中定义了 3 种认证策略的实现:
AuthenticationStrategy class
描述
AtLeastOneSuccessfulStrategy
只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功
FirstSuccessfulStrategy
第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略
AllSuccessfulStrategy
所有 Realm 成功,认证才视为成功
ModularRealmAuthenticator 内置的认证策略默认实现是AtLeastOneSuccessfulStrategy 方式。可以通过配置修改策略

多个realm代码实现

    //配置 SecurityManager
     @Bean
     public DefaultWebSecurityManager defaultWebSecurityManager(){
          //1 创建 defaultWebSecurityManager 对象
          DefaultWebSecurityManager defaultWebSecurityManager = new
                  DefaultWebSecurityManager();
          //2 创建认证对象,并设置认证策略
          ModularRealmAuthenticator modularRealmAuthenticator = new
                  ModularRealmAuthenticator();
          modularRealmAuthenticator.setAuthenticationStrategy(new
                  AllSuccessfulStrategy());

          defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator)
          ;
          //3 封装 myRealm 集合
          List<Realm> list = new ArrayList<>();
          list.add(myRealm);
          list.add(myRealm2);

          //4 将 myRealm 存入 defaultWebSecurityManager 对象
          defaultWebSecurityManager.setRealms(list);
          //5 返回
          return defaultWebSecurityManager;
     }

remember me 功能

Shiro 提供了记住我(RememberMe)的功能,比如访问一些网站时,关闭了浏览器, 下次再打开时还是能记住你是谁, 下次访问时无需再登录即可访问。

基本流程

  1. 首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会把 RememberMe 的 Cookie 写到客户端并保存下来;
  2. 关闭浏览器再重新打开;会发现浏览器还是记住你的;
  3. 访问一般的网页服务器端,仍然知道你是谁,且能正常访问;
  4. 但是,如果我们访问电商平台时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。

代码实现 

修改配置类
import com.example.demo.component.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
      @Autowired
      private MyRealm myRealm;

      //配置 SecurityManager
      @Bean
      public DefaultWebSecurityManager defaultWebSecurityManager() {
          //1 创建 defaultWebSecurityManager 对象
          DefaultWebSecurityManager defaultWebSecurityManager = new
                  DefaultWebSecurityManager();
          //2 创建加密对象,并设置相关属性
          HashedCredentialsMatcher matcher = new
                  HashedCredentialsMatcher();
          //2.1 采用 md5 加密
          matcher.setHashAlgorithmName("md5");
          //2.2 迭代加密次数
          matcher.setHashIterations(3);
          //3 将加密对象存储到 myRealm 中
          myRealm.setCredentialsMatcher(matcher);
          //4 将 myRealm 存入 defaultWebSecurityManager 对象
          defaultWebSecurityManager.setRealm(myRealm);
          //4.5 设置 rememberMe
          defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
          //5 返回
          return defaultWebSecurityManager;
      }
    //cookie 属性设置
    public SimpleCookie rememberMeCookie(){
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        //设置跨域
        //cookie.setDomain(domain);
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(30*24*60*60);
        return cookie;
    }
    //创建 Shiro 的 cookie 管理对象
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new
                CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());

        cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
        return cookieRememberMeManager;
    }


      //配置 Shiro 内置过滤器拦截范围
      @Bean
      public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
          DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
          //设置不认证可以访问的资源
          definition.addPathDefinition("/myController/userLogin", "anon");
          definition.addPathDefinition("/myController/login", "anon");
          definition.addPathDefinition("/login", "anon");
          //设置需要进行登录认证的拦截范围
          definition.addPathDefinition("/**", "authc");
          //添加存在用户的过滤器(rememberMe)
          definition.addPathDefinition("/**","user");
          return definition;
      }

}
修改 controller
//登录认证
    @GetMapping("userLogin")
    public String userLogin(String name, String pwd,@RequestParam(defaultValue =
            "false")boolean rememberMe, HttpSession session){
        //1 获取 Subject 对象
        Subject subject = SecurityUtils.getSubject();
        //2 封装请求数据到 token 对象中
        AuthenticationToken token = new UsernamePasswordToken(name,pwd,rememberMe);
        //3 调用 login 方法进行登录认证
        try {
            subject.login(token);
            session.setAttribute("user",token.getPrincipal().toString());
            return "main";
        } catch (AuthenticationException e) {
            e.printStackTrace();
            System.out.println("登录失败");
            return "登录失败";
        }
    }
    //登录认证验证 rememberMe
    @GetMapping("userLoginRm")
    public String userLogin(HttpSession session) {
        session.setAttribute("user","rememberMe");
        return "main";
    }
改造页面 login.html
<body>
 <h1>Shiro 登录认证</h1>
 <br>
 <form action="/myController/userLogin">
 <div>用户名:<input type="text" name="name" value=""></div>
 <div>密码:<input type="password" name="pwd" value=""></div>
 <div>记住用户:<input type="checkbox" name="rememberMe" 
value="true"></div>
 <div><input type="submit" value="登录"></div>
 </form>
</body>

测试

通过地址访问 userLoginRm http://localhost:8080/myController/userLoginRm

登录勾选记住用户

重新访问 userLoginRm http://localhost:8080/myController/userLoginRm

用户登录认证后登出 

用户登录后,配套的有登出操作。直接通过Shiro过滤器即可实现登出

代码实现

修改登录后的 main.html
<body>
 <h1>Shiro 登录认证后主页面</h1>
 <br>
 登录用户为:<span th:text="${session.user}"></span>
 <br>
 <a href="/logout">登出</a>
</body>
修改配置类,添加 logout 过滤器
//配置 Shiro 内置过滤器拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
 DefaultShiroFilterChainDefinition definition = new 
DefaultShiroFilterChainDefinition();
 //设置不认证可以访问的资源
 definition.addPathDefinition("/myController/userLogin","anon");
 definition.addPathDefinition("/myController/login","anon");
 //配置登出过滤器
 definition.addPathDefinition("/logout","logout");
 //设置需要进行登录认证的拦截范围
 definition.addPathDefinition("/**","authc");
 //添加存在用户的过滤器(rememberMe)
 definition.addPathDefinition("/**","user");
 return definition;
}

测试

点击“登出”登出系统

角色授权

授权

用户登录后,需要验证是否具有指定角色指定权限。Shiro也提供了方便的工具进行判断。 这个工具就是Realm的doGetAuthorizationInfo方法进行判断。触发权限判断的有两种方式:
  • 在页面中通过shiro:****属性判断
  • 在接口服务中通过注解@Requires****进行判断

后端接口服务注解 

通过给接口服务方法添加注解可以实现权限校验,可以加在控制器方法上,也可以加在业务方法上,一般加在控制器方法上。常用注解如下:
@RequiresAuthentication
验证用户是否登录,等同于方法subject.isAuthenticated()
@RequiresUser
验证用户是否被记忆:
登录认证成功subject.isAuthenticated()为true
登录后被记忆subject.isRemembered()为true
@RequiresGuest
验证是否是一个guest的请求,是否是游客的请求此时subject.getPrincipal()为null
@RequiresRoles
验证subject是否有相应角色,有角色访问方法,没有则会抛出异常 AuthorizationException。
@RequiresPermissions
验证subject是否有相应权限,有权限访问方法,没有则会抛出异常
AuthorizationException。

授权验证-没有角色无法访问

添加验证角色注解

//登录认证验证角色
@RequiresRoles("admin")
@GetMapping("userLoginRoles")
@ResponseBody
public String userLoginRoles() {
 System.out.println("登录认证验证角色");
 return "验证角色成功";
}

修改 main.html

<body>
 <h1>Shiro 登录认证后主页面</h1>
 <br>
 登录用户为:<span th:text="${session.user}"></span>
 <br>
 <a href="/logout">登出</a>
<br>
 <a href="/myController/userLoginRoles">测试授权</a>
</body>

修改 MyRealm 方法

//自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
@Override
protected AuthorizationInfo 
doGetAuthorizationInfo(PrincipalCollection principalCollection) {
 System.out.println("进入自定义授权方法");
 return null;
}

测试

授权验证-获取角色进行验证  

修改 MyRealm 方法

 //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
     @Override
     protected AuthorizationInfo
     doGetAuthorizationInfo(PrincipalCollection principalCollection) {
          SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
          //2 存储角色
          info.addRole("admin");
          //返回
          return info;
     }

运行测试

 确认库表

CREATE TABLE `role` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
 `name` VARCHAR(30) DEFAULT NULL COMMENT '角色名',
 `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
 `realname` VARCHAR(20) DEFAULT NULL COMMENT '角色显示名',
 PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色表';

CREATE TABLE `role_user` (
 `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
 `uid` BIGINT(20) DEFAULT NULL COMMENT '用户 id',
 `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
 PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色用户映射表';

查询 sql 

根据用户名查询对应角色信息
SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE 
uid=(SELECT id FROM USER WHERE NAME='张三'));

mapper 方法

 @Select("SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE uid=(SELECT id FROM USER WHERE NAME=#{principal}))")
    List<String> getUserRoleInfoMapper(@Param("principal") String principal);

service 实现

//获取用户的角色信息
    @Override
    public List<String> getUserRoleInfo(String principal) {
        return userMapper.getUserRoleInfoMapper(principal);
    }

MyRealm 

//自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
     @Override
     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
          System.out.println("进入自定义授权方法");
          //获取当前用户身份信息
          String principal =
                  principalCollection.getPrimaryPrincipal().toString();
          //调用接口方法获取用户的角色信息
          List<String> roles = userService.getUserRoleInfo(principal);
          System.out.println("当前用户角色信息:"+roles);
          //创建对象,存储当前登录的用户的权限和角色
          SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
          //存储角色
          info.addRoles(roles);
          //返回
          return info;
     }

配置类

    // 防止页面访问多次重定向
    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){

        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);

        return defaultAdvisorAutoProxyCreator;
    }

测试

启动登录测试

授权验证-获取权限进行验证 

获取权限验证和获取角色相类似

确认库表

CREATE TABLE `permissions` (
 `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
 `name` VARCHAR(30) DEFAULT NULL COMMENT '权限名',
 `info` VARCHAR(30) DEFAULT NULL COMMENT '权限信息',
 `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
 PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='权限表';

CREATE TABLE `role_ps` (
 `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
 `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
 `pid` BIGINT(20) DEFAULT NULL COMMENT '权限 id',
 PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色权限映射表';

查询 sql 

根据角色名查询对应权限信息
SELECT info FROM permissions WHERE id IN (SELECT pid FROM role_ps WHERE rid 
IN (SELECT id FROM role WHERE NAME IN('admin','userMag')));

mapper 方法

@Select({
            "<script>",
            "select info FROM permissions WHERE id IN ",
            "(SELECT pid FROM role_ps WHERE rid IN (",
            "SELECT id FROM role WHERE NAME IN ",
            "<foreach collection='roles' item='name' open='(' separator=',' close=')'>",
            "#{name}",
            "</foreach>",
            "))",
            "</script>"
            })
    List<String> getUserPermissionInfoMapper(@Param("roles")List<String> roles);

service 实现

//获取用户角色的权限信息
    @Override
    public List<String> getUserPermissionInfo(List<String> roles) {
        return userMapper.getUserPermissionInfoMapper(roles);
    }

MyRealm 方法

//自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
     @Override
     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
          System.out.println("进入自定义授权方法");
          //获取当前用户身份信息
          String principal = principalCollection.getPrimaryPrincipal().toString();
          //调用接口方法获取用户的角色信息
          List<String> roles = userService.getUserRoleInfo(principal);
          System.out.println("当前用户角色信息:"+roles);
          //调用接口方法获取用户角色的权限信息
          List<String> permissions =
                  userService.getUserPermissionInfo(roles);
          System.out.println("当前用户权限信息:"+permissions);
          //创建对象,存储当前登录的用户的权限和角色
          SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
          //存储角色
          info.addRoles(roles);
          //存储权限信息
          info.addStringPermissions(permissions);
          //返回
          return info;
     }

添加 controller 方法

//登录认证验证权限
    @RequiresPermissions("user:delete")
    @GetMapping("userPermissions")
    @ResponseBody
    public String userLoginPermissions() {
        System.out.println("登录认证验证权限");
        return "验证权限成功";
    }

改造 main.html

<body>
<h1>Shiro 登录认证后主页面</h1>
<br>
登录用户为:<span th:text="${session.user}"></span>
<br>
<a href="/logout">登出</a>
<br>
<a href="/myController/userLoginRoles">测试授权-角色验证</a>
<br>
<a href="/myController/userPermissions">测试授权-权限验证</a>
</body>

测试

启动登录测试

授权验证-异常处理 

创建认证异常处理类,使用@ControllerAdvice 加@ExceptionHandler 实现特殊异常处理。
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class PermissionsException {
   @ResponseBody
   @ExceptionHandler(UnauthorizedException.class)
   public String unauthorizedException(Exception ex){
     return "无权限";
   }
   @ResponseBody
   @ExceptionHandler(AuthorizationException.class)
   public String authorizationException(Exception ex){
     return "权限认证失败";
   }
}

测试

启动运行,用李四登录测试。

前端页面授权验证

引入依赖

<!--配置 Thymeleaf 与 Shrio 的整合依赖-->
<dependency>
 <groupId>com.github.theborakompanioni</groupId>
 <artifactId>thymeleaf-extras-shiro</artifactId>
 <version>2.0.0</version>
</dependency>

配置类添加新配置

用于解析 thymeleaf 中的 shiro: 相关属性
@Bean
public ShiroDialect shiroDialect(){
 return new ShiroDialect();
}
T hymeleaf 中常用的 shiro:属性:
guest 标签
<shiro:guest>
</shiro:guest>
用户没有身份验证时显示相应信息,即游客访问信息。
user 标签
<shiro: user >
</shiro:user>
用户已经身份验证 / 记住我登录后显示相应的信息。
authenticated 标签
<shiro:authenticated>
</shiro:authenticated>
用户已经身份验证通过,即 Subject.login 登录成功,不是记住我登录的。
notAuthenticated 标签
<shiro:notAuthenticated>
</shiro:notAuthenticated>
用户已经身份验证通过,即没有调用 Subject.login 进行登录,包括记住我自动登录的也属于未进行身份验证。
principal 标签
<shiro: principal/>
<shiro:principal property="username"/>
相当于 ((User)Subject.getPrincipals()).getUsername()
lacksPermission 标签
<shiro:lacksPermission name="org:create">
</shiro:lacksPermission>
如果当前 Subject 没有权限将显示 body 体内容。
hasRole 标签
<shiro:hasRole name="admin">
</shiro:hasRole>
如果当前 Subject 有角色将显示 body 体内容。
hasAnyRoles 标签
<shiro:hasAnyRoles name="admin,user">
</shiro:hasAnyRoles>
如果当前 Subject 有任意一个角色(或的关系)将显示 body 体内容。
lacksRole 标签
<shiro:lacksRole name="abc">
</shiro:lacksRole>
如果当前 Subject 没有角色将显示 body 体内容。
hasPermission 标签
<shiro:hasPermission name="user:create">
</shiro:hasPermission>
如果当前 Subject 有权限将显示 body 体内容

改造 main.html

<body>
<h1>Shiro 登录认证后主页面</h1>
<br>
登录用户为:<span th:text="${session.user}"></span>
<br>
<a href="/logout">登出</a>
<br>
<a shiro:hasRole="admin" href="/myController/userLoginRoles">测试
  授权-角色验证</a>
<br>
<a shiro:hasPermission="user:delete"
   href="/myController/userPermissions">测试授权-权限验证</a>
</body>

测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值