上一篇《【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限》介绍了实现Shiro的基础认证。本篇谈谈实现Shiro的基础授权。
需求:
① 某系统有公共模块、领导模块、管理员模块三个业务模块,均需要登录系统后才可以访问。
② admin、leader、employee三个人职位分别是管理员、领导、员工,均可登录系统。
③ 不同职位的人登录系统后,能看到的功能模块不同。管理员可以访问全部三个模块。领导可以访问除去管理员模块外的两个模块。员工只能访问公共模块。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
分析:
典型的运用授权权限的需求,继续考虑使用Shiro。
问题1、认证和授权怎么理解呢?
答:一点粗浅理解,比如通过了美国的签证能进入美国了,这就是获得了认证。
但是进入美国了,也只能去有授权的地方玩玩,五角大楼能进么?没有授权是不给进的。
所以,授权是在认证获得后进一步的安全管理。
问题2、需求在描述什么场景?
答:需求中包含了基于角色的权限访问控制RBAC(Role-Based Access Control)的设计思路。
简单来说,单个人对某某资源可操作。
进一步考虑,如果是多个人对某某资源可操作呢?需要重复的这样设置么?运用归纳思想,把这样的多个人归为一类,形成了角色的概念。即这一角色的多个人对某某资源可操作。
RBAC认为权限授权实际上是Who、What、How的问题。在RBAC模型中,who、what、how构成了访问权限三元组,也就是“Who对What(Which)进行How的操作”。
问题3、针对本需求的RBAC设计是怎么样的?
答:简化设计为:用户和角色为多对一关系、角色和资源为多对多关系
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0、数据库建表init.sql
-- 初始化
DROP TABLE sys_user;
DROP TABLE sys_role;
DROP TABLE sys_resource;
DROP TABLE sys_role_resource; -- 用户信息表
CREATE TABLE sys_user
(
userid INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号',
username VARCHAR(10) NOT NULL COMMENT '用户名称',
`password` VARCHAR(10) NOT NULL COMMENT '用户密码',
roleid INT NOT NULL COMMENT '角色编号'
); INSERT INTO sys_user VALUES(NULL, 'admin', '', 1), (NULL, 'leader', '', 2), (NULL, 'employee', '', 3); SELECT * FROM sys_user; -- 角色信息表
CREATE TABLE sys_role
(
roleid INT AUTO_INCREMENT PRIMARY KEY COMMENT '角色编号',
rolename VARCHAR(10) NOT NULL COMMENT '角色名称'
); INSERT INTO sys_role VALUES(NULL, '管理员'), (NULL, '领导'), (NULL, '员工'); SELECT * FROM sys_role; -- 资源信息表
CREATE TABLE sys_resource
(
resourceid INT AUTO_INCREMENT PRIMARY KEY COMMENT '资源编号',
resourcename VARCHAR(10) NOT NULL COMMENT '资源名称',
resourceurl VARCHAR(50) NOT NULL COMMENT '资源URL'
); INSERT INTO sys_resource VALUES
(NULL, '公共模块', 'publicModule'),
(NULL, '领导模块', 'leaderModule'),
(NULL, '管理员模块', 'adminModule'); SELECT * FROM sys_resource; -- 角色资源关联表
CREATE TABLE sys_role_resource
(
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '关联编号',
roleid INT NOT NULL COMMENT '角色编号',
resourceid INT NOT NULL COMMENT '资源编号'
); INSERT INTO sys_role_resource VALUES
(NULL, 1, 1), (NULL, 1, 2), (NULL, 1, 3),
(NULL, 2, 1), (NULL, 2, 2),
(NULL, 3, 1); SELECT * FROM sys_role_resource; -- 获取用户能访问的资源URL
SELECT u.userid, rs.resourceurl
FROM sys_role_resource AS rr
INNER JOIN sys_resource AS rs ON rr.resourceid = rs.resourceid
INNER JOIN sys_role AS r ON rr.roleid = r.roleid
INNER JOIN sys_user AS u ON u.roleid = r.roleid
WHERE u.userid = 1;
1、编写项目对象模型文件pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>cn.temptation</groupId>
<artifactId>studyShiro</artifactId>
<version>1.0-SNAPSHOT</version> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent> <dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>
<!-- mariadb -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.2.5</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 热启动 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2、编写项目配置文件application.properties
# 数据库访问配置
# 对应MariaDB驱动
spring.datasource.driverClassName=org.mariadb.jdbc.Driver # 数据源配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test
spring.datasource.username=root
spring.datasource.password=sa # 配置Springboot默认支持的Hikari数据库连接池
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1 # Spring Data JPA配置
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true # 格式化输出的json字符串
spring.jackson.serialization.indent_output=true # 设置控制台彩色打印
spring.output.ansi.enabled=ALWAYS
3、编写项目启动类Application.java
package cn.temptation; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class Application {
public static void main(String[] args) {
// SpringBoot项目启动
SpringApplication.run(Application.class, args);
}
}
4、编写全局异常处理类GlobalExceptionHandler.java
package cn.temptation.util; import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; /**
* 全局异常处理类
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public String errorHandler(Exception exception) {
return "redirect:/error/500";
}
}
5、编写错误页配置类ErrorPageConfig.java 和 错误页控制器ErrorController.java
错误页配置类ErrorPageConfig.java
package cn.temptation.util; import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component; /**
* 错误页配置类
*/
@Component
public class ErrorPageConfig implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
// 错误类型为401(无访问权限),显示401.html页面
ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401"); // 错误类型为404(找不到资源),显示404.html页面
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"); // 错误类型为500(服务器内部错误),显示500.html页面
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"); errorPageRegistry.addErrorPages(errorPage401, errorPage404, errorPage500);
}
}
错误页控制器ErrorController.java
package cn.temptation.util; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; /**
* 错误页控制器
*/
@Controller
@RequestMapping("/error")
public class ErrorController {
// 401页面
@GetMapping(value = "/401")
public String error_401() {
return "error/error_401";
} // 404页面
@GetMapping(value = "/404")
public String error_404() {
return "error/error_404";
} // 500页面
@GetMapping(value = "/500")
public String error_500() {
return "error/error_500";
}
}
6、编写错误页error_401.html、error_404.html 和 error_500.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="5;URL=/login">
<title>401</title>
<style>
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
} ::selection {
background: #b3d4fc;
text-shadow: none;
} html {
padding: 30px 10px;
font-size: 20px;
line-height: 1.4;
color: #737373;
background: #f0f0f0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
} body {
max-width: 550px;
_width: 550px;
padding: 30px 20px 50px;
border: 1px solid #b3b3b3;
border-radius: 4px;
margin: 0 auto;
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
background: #fcfcfc;
} h1 {
margin: 0 10px;
font-size: 50px;
text-align: center;
} h1 span {
color: #bbb;
} h3 {
margin: 1.5em 0 0.5em;
} p {
margin: 1em 0;
} ul {
padding: 0 0 0 40px;
margin: 1em 0;
} .container {
max-width: 500px;
_width: 500px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<h1>没有授权</h1>
<p>抱歉,您没有授权访问该页面</p>
</div>
</body>
</html>
error_401
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="5;URL=/login">
<title>404</title>
<style>
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
} ::selection {
background: #b3d4fc;
text-shadow: none;
} html {
padding: 30px 10px;
font-size: 20px;
line-height: 1.4;
color: #737373;
background: #f0f0f0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
} body {
max-width: 550px;
_width: 550px;
padding: 30px 20px 50px;
border: 1px solid #b3b3b3;
border-radius: 4px;
margin: 0 auto;
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
background: #fcfcfc;
} h1 {
margin: 0 10px;
font-size: 50px;
text-align: center;
} h1 span {
color: #bbb;
} h3 {
margin: 1.5em 0 0.5em;
} p {
margin: 1em 0;
} ul {
padding: 0 0 0 40px;
margin: 1em 0;
} .container {
max-width: 500px;
_width: 500px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<h1>没有找到<span>:(</span></h1>
<p>抱歉,您试图访问的页面不存在</p>
<p>可能是如下原因:</p>
<ul>
<li>一个错误的地址</li>
<li>一个过时的链接</li>
</ul>
</div>
</body>
</html>
error_404
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="5;URL=/login">
<title>500</title>
<style>
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
} ::selection {
background: #b3d4fc;
text-shadow: none;
} html {
padding: 30px 10px;
font-size: 20px;
line-height: 1.4;
color: #737373;
background: #f0f0f0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
} body {
max-width: 550px;
_width: 550px;
padding: 30px 20px 50px;
border: 1px solid #b3b3b3;
border-radius: 4px;
margin: 0 auto;
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
background: #fcfcfc;
} h1 {
margin: 0 10px;
font-size: 50px;
text-align: center;
} h1 span {
color: #bbb;
} h3 {
margin: 1.5em 0 0.5em;
} p {
margin: 1em 0;
} ul {
padding: 0 0 0 40px;
margin: 1em 0;
} .container {
max-width: 500px;
_width: 500px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<h1>内部错误</h1>
<p>抱歉,服务器上出现了错误......</p>
</div>
</body>
</html>
error_500
6、编写登录页面login.html、首页页面index.html、公共模块页page_public.html、领导模块页page_leader.html 和 管理员模块页page_admin.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>系统登录</title>
</head>
<body>
<div th:text="${msg}" style="color: red"></div>
<form action="doLogin" method="post">
帐号:<input type="text" id="txtUsername" name="username" /><br/>
密码:<input type="password" id="txtPassword" name="password" /><br/><br/>
<input type="submit" value="提交" /> <input type="reset" value="重置" />
</form>
</body>
</html>
登录页
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>系统首页</title>
</head>
<body>
<div th:text="${'欢迎您,' + currentuser}" style="color: red;float: left;"></div>
<div style="color: red;float: right;"><a href="doLogout">注销</a></div>
<!--
Thymeleaf中使用Shiro标签,具备授权才能看见
注意:如果不适用Shiro标签,没有授权的访问将产生401响应吗,执行ErrorPageConfig类 和 ErrorController类处理
-->
<!--<div style="clear: both;">公共模块:<a href="publicModule">公共模块</a></div>-->
<!--<div style="clear: both;">领导模块:<a href="leaderModule">领导模块</a></div>-->
<!--<div style="clear: both;">管理员模块:<a href="adminModule">管理员模块</a></div>-->
<div style="clear: both;" shiro:hasPermission="user:publicModule">公共模块:<a href="publicModule">公共模块</a></div>
<div style="clear: both;" shiro:hasPermission="user:leaderModule">领导模块:<a href="leaderModule">领导模块</a></div>
<div style="clear: both;" shiro:hasPermission="user:adminModule">管理员模块:<a href="adminModule">管理员模块</a></div>
</body>
</html>
首页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>公共模块</title>
</head>
<body>
公共模块(管理员、领导、员工均可访问)
</body>
</html>
公共模块页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>领导模块</title>
</head>
<body>
领导模块(管理员、领导均可访问)
</body>
</html>
领导模块页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>管理员模块</title>
</head>
<body>
管理员模块(管理员可访问)
</body>
</html>
管理员模块页
7、编写Shiro框架用配置类ShiroConfig.java 和 自定义Realm类MyRealm.java
配置类ShiroConfig.java
package cn.temptation.shiro; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.temptation.dao.ResourceDao;
import cn.temptation.domain.Resource;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; /**
* Shiro配置类
*/
@Configuration
public class ShiroConfig {
@Autowired
private ResourceDao resourceDao; // 1、创建ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); // 设置登录跳转页面
shiroFilterFactoryBean.setLoginUrl("/login"); /**
* Shiro内置过滤器:实现权限相关的拦截
* 常用过滤器:
* anon(认证用):无需认证(登录)即可访问
* authc(认证用):必须认证才可访问
* user(少用):使用rememberMe功能可以访问
* perms(授权用):必须得到资源权限才可访问
* role(授权用):必须得到角色权限才可访问
*/
Map<String, String> filterMap = new LinkedHashMap<>(); // 放行登录请求
filterMap.put("/doLogin", "anon"); // 配置退出过滤器,退出代码Shiro已经实现
filterMap.put("/logout", "logout"); // 配置授权过滤器 // 先代码写死,测试下
// filterMap.put("/publicModule", "perms[user:publicModule]");
// filterMap.put("/leaderModule", "perms[user:leaderModule]");
// filterMap.put("/adminModule", "perms[user:adminModule]"); // 获取所有资源,并配置需要进行授权过滤的资源
List<Resource> resources = resourceDao.findAll();
resources.forEach(item -> {
if (!"".equals(item.getResourceurl())) {
filterMap.put("/" + item.getResourceurl(), "perms[user:" + item.getResourceurl() + "]");
}
}); // 过滤链定义,从上向下顺序执行,一般将/*放在最下边
filterMap.put("/*", "authc"); // 设置未授权界面
shiroFilterFactoryBean.setUnauthorizedUrl("/error/401"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); return shiroFilterFactoryBean;
} // 2、创建DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("myRealm") MyRealm myRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); // 关联Realm
defaultWebSecurityManager.setRealm(myRealm); return defaultWebSecurityManager;
} // 3、创建Realm
@Bean(name = "myRealm")
public MyRealm getRealm() {
return new MyRealm();
} // 4、配置ShiroDialect后,可以在页面使用Shiro标签
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
}
自定义Realm类MyRealm.java
package cn.temptation.shiro; import cn.temptation.dao.ResourceDao;
import cn.temptation.dao.UserDao;
import cn.temptation.domain.User;
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.springframework.beans.factory.annotation.Autowired; import java.util.List; /**
* 自定义Realm
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserDao userDao;
@Autowired
private ResourceDao resourceDao; // 授权处理
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取当前登录获得认证的用户
User user = (User) principalCollection.getPrimaryPrincipal();
// 下句语句会抛出异常交由ErrorController类根据ErrorPageConfig类中注册的响应码和错误页面处理
// System.out.println(1 / 0); if (user != null) {
// 给资源授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 先代码写死,测试下
// info.addStringPermission("user:publicModule");
// info.addStringPermission("user:leaderModule");
// info.addStringPermission("user:adminModule"); // 根据获得认证的用户编号查询该用户具备的资源URL集合
List<String> resourceurls = resourceDao.findByUserid(user.getUserid()); // 遍历集合,组装成满足授权过滤器过滤格式,并添加到资源信息中
resourceurls.forEach(item -> info.addStringPermission("user:" + item)); return info;
} return null;
} // 认证处理
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 编写Shiro判断逻辑,判断账号和密码
// 1、判断账号
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; User user = userDao.findByUsername(token.getUsername());
if (user == null) {
// 账号错误,Shiro底层会抛出UnknownAccountException异常
return null;
} // 2、判断密码
// 只做认证,principal可以设置为空字符串
// return new SimpleAuthenticationInfo("", user.getPassword(), "");
// 认证后做授权处理,需要将获得认证的用户对象赋值给principal,授权处理时会用到
return new SimpleAuthenticationInfo(user, user.getPassword(), "");
}
}
8、编写用户实体类User.java、角色实体类Role.java 和 资源实体类Resource.java
用户实体类User.java
package cn.temptation.domain; import javax.persistence.*; @Entity
@Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "userid")
private Integer userid; @Column(name = "username")
private String username; @Column(name = "password")
private String password; @ManyToOne
@JoinColumn(name = "roleid", foreignKey = @ForeignKey(name = "none"))
private Role role; public Integer getUserid() {
return userid;
} public void setUserid(Integer userid) {
this.userid = userid;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public Role getRole() {
return role;
} public void setRole(Role role) {
this.role = role;
}
}
角色实体类Role.java
package cn.temptation.domain; import javax.persistence.*;
import java.util.Set; @Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "roleid")
private Integer roleid; @Column(name = "rolename")
private String rolename; @ManyToMany
@JoinTable(name = "sys_role_resource",
joinColumns = {@JoinColumn(name = "roleid", referencedColumnName = "roleid", foreignKey = @ForeignKey(name = "none"))},
inverseJoinColumns = {@JoinColumn(name = "resourceid", referencedColumnName = "resourceid", foreignKey = @ForeignKey(name = "none"))})
private Set<Resource> resources; public Integer getRoleid() {
return roleid;
} public void setRoleid(Integer roleid) {
this.roleid = roleid;
} public String getRolename() {
return rolename;
} public void setRolename(String rolename) {
this.rolename = rolename;
}
}
资源实体类Resource.java
package cn.temptation.domain; import javax.persistence.*; @Entity
@Table(name = "sys_resource")
public class Resource {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "resourceid")
private Integer resourceid; @Column(name = "resourcename")
private String resourcename; @Column(name = "resourceurl")
private String resourceurl; public Integer getResourceid() {
return resourceid;
} public void setResourceid(Integer resourceid) {
this.resourceid = resourceid;
} public String getResourcename() {
return resourcename;
} public void setResourcename(String resourcename) {
this.resourcename = resourcename;
} public String getResourceurl() {
return resourceurl;
} public void setResourceurl(String resourceurl) {
this.resourceurl = resourceurl;
}
}
9、编写用户控制器类UserController.java
package cn.temptation.web; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
public class UserController {
// 访问登录页
@RequestMapping("/login")
public String login() {
// 下句语句会抛出异常交由GlobalExceptionHandler类的errorHandler方法处理
// System.out.println(1 / 0); return "login";
} // 访问首页
@RequestMapping("/index")
public String index() {
return "index";
} // 访问公共模块
@RequestMapping("/publicModule")
public String publicModule() {
return "page_public";
} // 访问私密模块
@RequestMapping("/privateModule")
public String privateModule() {
return "page_leader";
} // 登录处理
@RequestMapping("/doLogin")
public String doLogin(String username, String password, Model model) {
// 使用Shiro编写认证处理
// 1、获取Subject
Subject subject = SecurityUtils.getSubject(); // 2、封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 3、执行登录
try {
// 登录成功
subject.login(token); // 返回当前用户的帐号
model.addAttribute("currentuser", token.getUsername()); return "index";
} catch (UnknownAccountException exception) {
// 返回错误信息
model.addAttribute("msg", "账号错误!"); return "login";
} catch (IncorrectCredentialsException exception) {
// 返回错误信息
model.addAttribute("msg", "密码错误!"); return "login";
}
} // 注销处理
@RequestMapping("/doLogout")
public String doLogout() {
// 1、获取Subject
Subject subject = SecurityUtils.getSubject(); // 2、执行注销
try {
subject.logout();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
return "login";
}
}
}
10、编写用户数据访问接口UserDao.java、角色数据访问接口RoleDao.java 和 资源数据访问接口ResourceDao.java
用户数据访问接口UserDao.java
package cn.temptation.dao; import cn.temptation.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; public interface UserDao extends JpaRepository<User, Integer> {
// 根据账号查询用户
@Query(value = "SELECT * FROM sys_user WHERE username=:username", nativeQuery = true)
User findByUsername(@Param("username") String username);
}
角色数据访问接口RoleDao.java
package cn.temptation.dao; import cn.temptation.domain.Role;
import org.springframework.data.jpa.repository.JpaRepository; public interface RoleDao extends JpaRepository<Role, Integer> { }
资源数据访问接口ResourceDao.java
package cn.temptation.dao; import cn.temptation.domain.Resource;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import java.util.List; public interface ResourceDao extends JpaRepository<Resource, Integer> {
@Query(value = "SELECT rs.resourceurl FROM sys_role_resource AS rr " +
"INNER JOIN sys_resource AS rs ON rr.resourceid = rs.resourceid " +
"INNER JOIN sys_role AS r ON rr.roleid = r.roleid " +
"INNER JOIN sys_user AS u ON u.roleid = r.roleid WHERE u.userid = :userid ", nativeQuery = true)
List<String> findByUserid(@Param("userid") Integer userid);
}
11、项目结构
12、运行效果
【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础授权权限的更多相关文章
-
【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限
开发环境搭建参见<[原]无脑操作:IDEA + maven + SpringBoot + JPA + Thymeleaf实现CRUD及分页> 需求: ① 除了登录页面,在地址栏直接访问其他 ...
-
【原】无脑操作:EasyUI Tree实现左键只选择叶子节点、右键浮动菜单实现增删改
Easyui中的Tree组件使用频率颇高,经常遇到的需求如下: 1.在树形结构上,只有叶子节点才能被选中,其他节点不能被选中: 2.在叶子节点上右键出现浮动菜单实现新增.删除.修改操作: 3.在非叶子 ...
-
【原】无脑操作:express + MySQL 实现CRUD
基于node.js的web开发框架express简单方便,很多项目中都在使用.这里结合MySQL数据库,实现最简单的CRUD操作. 开发环境: IDE:WebStorm DB:MySQL ------ ...
-
【原】无脑操作:eclipse + maven搭建SSM框架
网上看到一些Spring + Spring MVC + MyBatis框架的搭建教程,不是很详细或是时间久远了,自己动手整一个简单无脑的! 0.系统环境 1)Windows 10 企业版 2)JDK ...
-
【原】无脑操作:ElasticSearch学习笔记(01)
开篇来自于经典的“保安的哲学三问”(你是谁,在哪儿,要干嘛) 问题一.ElasticSearch是什么?有什么用处? 答:截至2018年12月28日,从ElasticSearch官网(https:// ...
-
Springboot+JPA+Thymeleaf 校园博客完整小网站
本文所属[知识林]:http://www.zslin.com/web/article/detail/35 此项目是一个比较简易的校园博客.麻雀虽小五脏俱全,虽然是比较简易的但是涉及的知识点还是比较全面 ...
-
【原】无脑操作:IDEA + maven + SpringBoot + JPA + Thymeleaf实现CRUD及分页
一.开发环境: 1.windows 7 企业版 2.IDEA 14 3.JDK 1.8 4.Maven 3.5.2 5.MariaDB 6.SQLYog 二.Maven设置: Maven目录下的con ...
-
【原】无脑操作:IDEA + maven + SpringBoot + JPA + EasyUI实现CRUD及分页
背景:上一篇文章的界面太丑.没有条件查询功能.所以做一些改进,整合EasyUI做实现.(仅以此文纪念表格中出现的这些朋友工作六周年,祭奠一下逝去的青春^_^) 一.开发环境(参照上一篇文章) 补充:E ...
-
【原】无脑操作:eclipse创建maven工程时,如何修改默认JDK版本?
问题描述:eclipse建立maven项目时,JDK版本默认是1.5,想创建时默认版本设置为1.8,如何修改? 解决方案: 找到本机maven仓库存放位置,比如:${user.home}/.m2/路径 ...
随机推荐
-
MWeb 2.0.7 版发布!
更新前针对 MAS 上的评论重点说一下:MWeb 是支持直接对本地文件夹操作的,不用导入到文档库!请使用外部模式!请使用外部模式!!请使用外部模式!!! 重要的话讲三次!使用方法是 CMD + E 打 ...
-
iframe 内显示的网页 只显示改网页的某一部分!
使用iframe调用指定网页的特定位置(显示目标网页某区域的我想要的内容) 有些时候我们并不需要显示iframe标签属性src指定的目标网页的所有内容,往往只需要显示某一特定区域.现有两种实现方法提供 ...
-
移除了css框架,世界干净了
在之前的webapp项目里,我使用了bootstrap作为三方的css库,只调取了其中一部分源码的less使用,大部分代码仍然是自己写的. 自己的代码也是参照bootstrap的目录结构和它的一些规范 ...
-
apache不解析php文档?提示需要下载(转)
在httpd.cong中 LoadModule php5_module modules/libphp5.so #这一行php5安装的时候就已经自动添加上了 AddType application/x- ...
-
Codeforces Round #350 (Div. 2)解题报告
codeforces 670A. Holidays 题目链接: http://codeforces.com/contest/670/problem/A 题意: A. Holidays On the p ...
-
ios的虚拟键盘与fixed移动端的bug
//$('#search')表单input;$('.search_out')浮动元素 var u = navigator.userAgent, app = navigator.appVersion;v ...
-
JavaScript获取页面宽度高度
网页可见区域宽:document.body.clientWidth网页可见区域高:document.body.clientHeight网页可见区域宽:document.body.offsetWidth ...
-
C语言宏定义##连接符和#符的使用
1. 关于宏(Macro) 属于编译器预处理的范畴,属于编译器概念(而非运行期概念). 2. 关于# #的功能:是 将其后面的宏参数进行 字符串化操作(Stringfication),即:在对它所引用 ...
-
4种事务的隔离级别,InnoDB怎样巧妙实现?
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/z50L2O08e2u4afToR9A/article/details/82186189 事务ACID ...
-
P2633 Count on a tree
思路 运用树上差分的思想,转化成一个普通的主席树模型即可求解 代码 #include <cstdio> #include <algorithm> #include <cs ...