1. 引入shiro依赖
注意: 我是采用thymeleaf做模板引擎,需要shiro标签做权限判断,所以引用了thymeleaf-extras-shiro依赖
<!--shiro支持-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
<!--thymeleaf中使用shiro标签做权限判断-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2. 自定义realm
代码如下:
package com.xxqy.shopping.realm;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxqy.shopping.entity.SysUser;
import com.xxqy.shopping.service.SysUserService;
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 javax.annotation.Resource;
import java.util.HashSet;
/**
* 自定义Realm
* @Author yww
* @CreateTime 2021-06-21
*
*/
public class CustomRealm extends AuthorizingRealm{
@Resource
private SysUserService sysUserService;
/**
* 授权方法
* 操作的时候,判断用户是否具有响应的权限
* 先认证 -- 安全数据
* 再授权 -- 根据安全数据获取用户具有的所有操作权限
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//1.获取已认证的用户数据
SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
//2.根据用户数据获取用户的权限信息(所有角色,所有权限)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//所有角色
HashSet<String> roles = new HashSet<>();
//所有权限
HashSet<String> perms = new HashSet<>();
//TODO 查询用户所有角色 设值给roles,所有权限 设值给perms
info.setStringPermissions(perms);
info.setRoles(roles);
return info;
}
/**
* 认证方法
* 参数: 传递的用户名密码
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.获取登录的用户名密码(token)
UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
String username = upToken.getUsername();
String password = new String(upToken.getPassword());
//2.根据用户名密码查询数据库,判断是否一致
SysUser sysUser = sysUserService.login(username, password);
//存在
if(sysUser != null){
//3.如果一致返回安全数据 构造方法 (安全数据,密码,realm域名)
return new SimpleAuthenticationInfo(sysUser,sysUser.getPassword(),"CustomRealm");
}
//4.不一致,返回null,抛出异常
return null;
}
}
3. 配置shiro
代码如下:
package com.xxqy.shopping.config;
import com.xxqy.shopping.realm.CustomRealm;
import net.minidev.json.reader.BeansWriter;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
/**
* shiro配置
* @Author yww
* @CreateTime 2021-06-21
*/
@Configuration
public class ShiroConfig {
//1.创建realm
@Bean
public CustomRealm getRealm(){
return new CustomRealm();
}
//2.创建安全管理器
@Bean
public SecurityManager getSecurityManager(CustomRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setSessionManager(sessionManager());
securityManager.setRealm(realm);
return securityManager;
}
//3.配置shiro的过滤器工厂
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
//1.创建过滤器工厂
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
//2.设置安全管理器
filterFactory.setSecurityManager(securityManager);
//3.通用配置 (跳转登录页面,未授权跳转的页面)
filterFactory.setLoginUrl("/login");//未登录跳转到登录页面
filterFactory.setUnauthorizedUrl("/login");//未授权跳转到未授权页面
//4.设置过滤器集合
LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/login","anon");//anon - 当前请求地址可以匿名访问 login页面
filterMap.put("/static/**","anon");//静态资源
filterMap.put("/sys/user/login","anon");//login请求
filterMap.put("/**","authc");//authc - 当前请求地址必须认证之后可以访问
filterFactory.setFilterChainDefinitionMap(filterMap);
return filterFactory;
}
/**
* 解决输入网址地址栏出现 jsessionid 的问题
*/
@Bean
public DefaultWebSessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//解决输入网址地址栏出现 jsessionid 的问题
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}
/**
* @RequiresPermissions(value = "permission::select")
* 基于注解注解的配置方式进行授权,一旦操作用户不具备操作权限,目标方法不会被执行,并且会抛出AuthorizationException异常。
*/
//4.开启对shiro注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
//注解生效2
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator app=new DefaultAdvisorAutoProxyCreator();
app.setProxyTargetClass(true);
return app;
}
//shiro方言生效,thymeleaf中使用shiro标签库
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
4. 登录
代码如下:
/**
* shiro登录
* 前端发送请求 => 接口部分获取用户名密码 => 通过subject.login => realm域的认证方法
* @param username 用户名
* @param password 密码
* @param captcha 验证码
* @return
*/
@RequestMapping("/login")
@ResponseBody
public Rest loginDo(String username, String password, String captcha){
try{
//1.构造登录令牌
UsernamePasswordToken upToken = new UsernamePasswordToken(username, password);
//2.获取subject,执行getSubject()进入自定义realm认证
Subject subject = SecurityUtils.getSubject();
//3.调用subject进行登录
subject.login(upToken);
return Rest.ok().setMsg("登录成功");
}catch (Exception e){
e.printStackTrace();
return Rest.error().setMsg("用户名或密码错误");
}
}
5. 注解授权
//shiro注解授权
@RequiresPermissions(value = "permission::select")
@RequestMapping("/sysPermissionList")
public String sysPermissionList(){
return "sys/sysPermissionList";
}
6. 因为使用shiro注解授权,没有权限会抛出AuthorizationException异常,所以要捕获特定异常
package com.xxqy.shopping.exception;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 自定义公共异常处理类
* @Author yww
* @CreateTime 2021-06-21
*/
@ControllerAdvice
public class BaseExceptionHandler {
/**
* shiro注解授权,未拥有权限会报AuthorizationException异常
* @param request
* @param response
* @param exception
* @return
*/
@ExceptionHandler(value = AuthorizationException.class)
@ResponseBody
public String error(HttpServletRequest request,HttpServletResponse response,AuthorizationException exception){
return "未授权,不能访问!!!";
}
}
效果图如下:
7. thymeleaf中使用shiro标签库判断权限
注意: 引入 xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
shiro:hashPermission 为判断权限标签
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" th:with="unixstamp=${#dates.createNow().time}">
<!--引用头部-->
<div th:include="common/common :: common"></div>
<body class="wholeBody">
<!--vue区域-->
<div id="app" v-cloak>
<el-form :inline="true" :model="condition" class="demo-form-inline">
<!--左侧搜索条件区域-->
<el-form-item>
<el-input v-model="condition.username" placeholder="用户名"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="search()">查询</el-button>
</el-form-item>
<!--右侧功能按钮区域-->
<el-form-item style="float: right;">
<el-button shiro:hasPermission="sys-user-save" type="primary" @click="add()">添加</el-button>
<el-button shiro:hasPermission="sys-user-delete" type="danger" @click="batchRemove()">批量删除</el-button>
<el-button shiro:hasPermission="sys-user-bindingrole" type="danger" @click="bindingRole()" style="background-color: #4dc7d8;border-color: #4dc7d8;">绑定角色</el-button>
</el-form-item>
</el-form>
<script type="text/javascript" th:src="@{/static/js/sys/sysUserList.js(v=${unixstamp})}"></script>
</body>
</html>