某天,我在想 有没有 boot 版的shiro,网上一顿扒拉也找不到自己想要的结果
于是我又去 maven 中央仓库一顿操作,发现真有boot版的shiro,
我舍弃以前的 springboot 整合 shiro代码【主要还是和SpringBoot没半毛钱关系,还要配置那么多,完全的spring整合shiro,没意思】,重新研究boot版的shiro
shiro官方整合的springboot文档:Apache Shiro | Simple. Java. Security.https://shiro.apache.org/spring-framework.html#configuration-properties
Dome案例免费下载学习,【共享,学习,免费】,原创不易,值得注意的是,不要使用spring 集成 shiro的思路去集成,这会导致你需要配置很多代码,比如 shiro提供的配置会失效,要使用SpringBoot来集成
免费下载dome地址
maven坐标
不需要额外引入springboot坐标,官方已经给我们整好了
<!--shiro-boot war 项目使用这个maven坐标库 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.8.0</version>
</dependency>
Shiro基础配置
自定义 Realm
自定义 Realm
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;
public class SampleRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
return new SimpleAuthenticationInfo(null, null, getName());
}
}
config配置
config配置
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
//chainDefinition.addPathDefinition("/api/doLogin", "anon");
//chainDefinition.addPathDefinition("/**", "authc");
chainDefinition.addPathDefinition("/**", "anon");
return chainDefinition;
}
@Bean("authorizer")
public SampleRealm sampleRealm() {
return new SampleRealm();
}
}
是的,到这里就已经完成了shiro基础了,剩下的就是业务代码
完整的代码
application.properties
#配置端口
server.port=80
#关闭spring自带日志
logging.level.org.apache.ignite=OFF
#使用自己的日志文件
logging.config=classpath:logback.xml
# jsp 配置
spring.mvc.view.prefix=/page/
spring.mvc.view.suffix=.jsp
#---------shiro配置--------------------------------------
#登录页面
shiro.loginUrl=/login
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itgsvip</groupId>
<artifactId>spring-boot-shiro-web-jsp</artifactId>
<version>0.0.1-alpha</version>
<packaging>war</packaging>
<description>
Shiro版快速集成springBoot和jsp
</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- springboot 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--shiro-boot war 项目使用这个maven坐标库 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.8.0</version>
</dependency>
<!-- jsp页面使用jstl标签 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- 用于编译jsp -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
ShiroConfig.java 配置类
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
/**
* <p>@方法名描述 : Shiro过滤器 </p>
* <per>
* <code>
* anon ===> 开放路径,允许匿名访问,不需要权限和不需要登录就可以访问
* authc ===> 需要登录后才可以访问路径
* </code>
* </per>
* @return
*/
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/api/doLogin", "anon");
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
/**
* <p>@方法名描述 :配置自定义Realm ,
* <per>
* Realm是必须配置的,并且name必须是authorizer,这是底层规定,
* 不配置异常如下:[ Parameter 0 of method authorizationAttributeSourceAdvisor
* in org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration
* required a bean named 'authorizer' that could not be found. ]
* </per>
* </p>
* @return
*/
@Bean("authorizer")
public SampleRealm sampleRealm() {
return new SampleRealm();
}
}
自定义 Realm SampleRealm.java
import java.util.HashSet;
import java.util.Set;
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.authc.UsernamePasswordToken;
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.springframework.beans.factory.annotation.Autowired;
import cn.itgsvip.pojo.UserVo;
import cn.itgsvip.service.IUserLoginService;
public class SampleRealm extends AuthorizingRealm {
@Autowired
private IUserLoginService iUserLoginService;
/**
* <p>@重写方法名描述 : 授权查询,有shiro注解的方法都会走这里,去验证权限 </p>
* @param principals SecurityUtils.getSubject()的集合
* @return 授权信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
/** 将PrimaryPrincipal转换为UserVo对象 **/
UserVo principal = (UserVo) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
/** 模拟管理员角色 **/
if ("admin".equals(principal.getUserName())) {
/** 用户角色 **/
authorizationInfo.addRole("admin");
/** 角色权限集合 **/
Set<String> permissions = new HashSet<String>();
permissions.add("admin:del");// 权限
permissions.add("admin:find");
authorizationInfo.addStringPermissions(permissions);
}
/** 模拟管用户角色 **/
if ("user".equals(principal.getUserName())) {
/** 用户角色 **/
authorizationInfo.addRole("user");
/** 角色权限集合 **/
Set<String> permissions = new HashSet<String>();
permissions.add("user:del");
permissions.add("user:find");
authorizationInfo.addStringPermissions(permissions);
}
/** 模拟拥有管理员和用户的角色 **/
if ("adminAndUser".equals(principal.getUserName())) {
/** 角色集合 **/
Set<String> roles = new HashSet<String>();
roles.add("admin");// 管理员角色
roles.add("user");// 用户角色
authorizationInfo.addRoles(roles);
/** 权限集合 **/
Set<String> permissions = new HashSet<String>();
permissions.add("admin:del");// 权限
permissions.add("admin:find");
permissions.add("user:del");
permissions.add("user:find");
authorizationInfo.addStringPermissions(permissions);
}
return authorizationInfo;
}
/**
* <p>@重写方法名描述 :登录认证信息 </p>
* @param authcToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String userName = token.getUsername();
String password = String.valueOf(token.getPassword());
UserVo users = iUserLoginService.dologin(userName, password);
return new SimpleAuthenticationInfo(users, password, getName());
}
}
UserVO,IUserLoginService 与业务实现代码
/** 用户id **/
private String userId;
/** 用户名 **/
private String userName;
/** 用户密码 **/
private String passWord;
/**
* <p>@方法名描述 : 用户登录方法 </p>
* @param userName 用户名
* @param password 密码
* @return 用户信息
*/
UserVo dologin(String userName, String password);
/**
* <p>@方法名描述 : 用户登录实现方法,模拟数据库数据 </p>
* @param userName 用户名
* @param password 密码
* @return 用户信息
*/
@Override
public UserVo dologin(String userName, String password) {
UserVo user = new UserVo();
if ("admin".equals(userName)) {
user.setUserId("10001");
user.setUserName(userName);
user.setPassWord(password);
}
/**模拟数据库账号,用户角色 **/
if ("user".equals(userName)) {
user.setUserId("10002");
user.setUserName(userName);
user.setPassWord(password);
}
/** 模拟数据库账号,拥有管理员角色和用户角色的账号 **/
if ("adminAndUser".equals(userName)) {
user.setUserId("10000");
user.setUserName(userName);
user.setPassWord(password);
}
return user;
}
Controller代码
AdminController.java
import java.util.Date;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin")
public class AdminController {
/**
* <p>@方法名描述 :管理员角色 可访问路径</p>
* @return
*/
@RequiresRoles("admin")
@RequestMapping
public String admimIndex() {
return "welcome to you , admin! Now it is " + new Date();
}
/**
* <p>@方法名描述 :拥有admin:find权限可访问路径 </p>
* @return
*/
@RequiresPermissions("admin:find")
@RequestMapping("find")
public String admimFind() {
return "You performed the delete operation";
}
/**
* <p>@方法名描述 :拥有 管理员和用户角色 和 admin:del 权限 可访问路径 </p>
* @return
*/
@RequiresRoles(logical = Logical.AND, value = { "admin", "user" })
@RequiresPermissions("admin:del")
@RequestMapping("del")
public String admimDel() {
return "You performed the delete operation";
}
}
UserController.java
import java.util.Date;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
/**
* <p>@方法名描述 :用户角色 可访问路径 </p>
* @return
*/
@RequiresRoles("user")
@RequestMapping
public String admimIndex() {
return "welcome to you , admin! Now it is " + new Date();
}
/**
* <p>@方法名描述 :拥有 user:find 权限可访问路径 </p>
* @return
*/
@RequiresPermissions("user:find")
@RequestMapping("find")
public String admimFind() {
return "You performed the delete operation";
}
/**
* <p>@方法名描述 :拥有 user:del 权限可访问路径 </p>
* @return
*/
@RequiresPermissions("user:del")
@RequestMapping("del")
public String admimDel() {
return "You performed the delete operation";
}
}
OsController.java,登录和基础页面
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class OsController {
/**
* <p>@方法名描述 : 首页 </p>
* @return 返回首页
*/
@RequestMapping("index")
public String index() { return "index"; }
/**
* <p>@方法名描述 : 登录页面 </p>
* @return 返回登录页面
*/
@RequestMapping("login")
public String login() { return "login"; }
/**
* <p>@方法名描述 : 登录接口 </p>
* @param username 用户名
* @param password 密码
* @return 登录成功转发到首页
*/
@RequestMapping("api/doLogin")
public String doLogin(String username, String password) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
/** 登录,交给Shiro处理,实际登录处理逻辑还是得自己写,处理了个寂寞 **/
SecurityUtils.getSubject().login(token);
return "redirect:/index";
}
/**
* <p>@方法名描述 :退出接口 </p>
* @return 转发到登录页面
*/
@RequestMapping("api/logout")
public String logout() {
/** 退出,交给Shiro处理 **/
SecurityUtils.getSubject().logout();
return "redirect:/login";
}
}
AuthorizationException无权限拦截
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
public class ShiroExceptionAdvice {
/**
* <p>@方法名描述 : 无权限异常 </p>
* @param e AuthorizationException异常
* @return 403无权限页面
*/
@ExceptionHandler(AuthorizationException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public Object handleException(AuthorizationException e, Model model) {
System.out.println("AuthorizationException================>" + e.getMessage());
model.addAttribute("code", HttpStatus.FORBIDDEN.value());
return "403";
}
}
jsp页面代码
login.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html >
<head >
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="viewport" content="width=device-width"/>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
rel="stylesheet" type="text/css"/>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"
type="text/css"/>
<style>
body {
margin-top: 60px;
}
.box {
padding: 50px;
text-align: center;
vertical-align: middle;
}
.custom-header {
/*background-color: #161616;*/
border: 2px solid #3254a0;
}
</style>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<p>Here are a few sample accounts to play with from the text-based Realm</p>
<table class="table">
<thead>
<tr>
<th>Username</th>
<th>Password</th>
<th>Roles</th>
</tr>
</thead>
<tbody>
<tr>
<td>admin</td>
<td>password</td>
<td>admin</td>
</tr>
<tr>
<td>user</td>
<td>password</td>
<td>user</td>
</tr>
<tr>
<td>adminAndUser</td>
<td>password</td>
<td>具备管理员和用户操作账号</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Login</h3>
</div>
<div class="panel-body">
<form name="loginform" action="api/doLogin" method="POST" accept-charset="UTF-8" role="form">
<fieldset>
<div class="form-group">
<input class="form-control" placeholder="Username or Email" name="username" value="admin"
type="text"/>
</div>
<div class="form-group">
<input class="form-control" placeholder="Password" name="password" type="password" value="password"/>
</div>
<input class="btn btn-lg btn-success btn-block" type="submit" value="Login"/>
</fieldset>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width" />
<link
href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
rel="stylesheet" type="text/css" />
<link
href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet" type="text/css" />
<style>
body {
margin-top: 60px;
}
.box {
padding: 50px;
text-align: center;
vertical-align: middle;
}
.custom-header {
/*background-color: #161616;*/
border: 2px solid #3254a0;
}
</style>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<h3>管理员权限操作</h3>
<a href="/admin">admin</a><br /> <a href="/admin/del">/admin/del-无权限操作</a><br />
<a href="/admin/find">/admin/find</a><br />
</div>
<div class="col-md-4 col-md-offset-4">
<h3>用户权限操作</h3>
<a href="/user">用户页面</a><br /> <a href="/user/del">用户删除操作</a><br />
</div>
<div class="col-md-4 col-md-offset-4">
<h4>拥有管理员和用户权限的操作</h4>
<a href="/admin/del">用户删除操作</a><br />
</div>
<div class="col-md-4 col-md-offset-4">
<a href="/login">登录页面</a><br />
</div>
<div class="col-md-4 col-md-offset-4">
<a href="/api/logout">退出登录页面</a><br />
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery.js"></script>
<script
src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
</body>
</html>
403.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>你无权限操作~~~</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width" />
<link
href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
rel="stylesheet" type="text/css" />
<link
href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="box col-md-6 col-md-offset-3">
<div class="custom-header">
<img
src="http://shiro.apache.org/assets/images/apache-shiro-logo.png" />
</div>
<div class="logo">
<h1>${ code }</h1>
</div>
<p class="lead text-muted">你的权限不足,充值一个998吧</p>
<a href="/" class="btn btn-primary">Go Home</a>
</div>
</div>
</div>
</body>
</html>
下一篇:《springBoot快速 整合shiro+shiro-redis+thymeleaf》
基于 shiro-redis-spring-boot-starter
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis-spring-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>