一、新建 springboot 我的项目
1、
new–>Project–>Spring Initralizr
Group:com.zb
Artifact:zbook
springboot version:2.0.4
2、
将
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
改成
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3、此时我的项目构造
maven clean 一下,右键 ZbookApplication 运行,我的项目就跑起来了,就是这么简略,真正做到了开箱即用。
二、RestFul Api 接口
1、在 controller 包下新建 HomeController
package com.zb.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {@RequestMapping("/")
public String home(){return "Hello, Zbook!";}
}
注:RestController 和 Controller 注解的区别是:RestController 是返回的内容就是返回的内容,相当于加个 @ResponseBody, 而 controller 个别是返回的页面
此时关上网页,输出 http://localhost:8080/
就会看到 Hello,Zbook!
三、集成 Thymleaf
1、
下面的能够当作是提供服务的接口,假如咱们要开发一个 web 利用,springboot 默认是集成的 thymleaf。
springboot 是约定大于配置的,咱们来看看对于 thymleaf 的约定
(1)默认动态文件(js,css,jpg 等)放在 resources 上面的 static 文件夹上面
(2)页面文件放在 templates 文件夹上面
咱们采纳 bootstrap 来渲染页面,如下图
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title> 登录 </title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<form action="login_in" method="post" class="form-horizontal" role="form" >
<div class="form-group">
<h2 class="col-sm-offset-5 col-sm-4"> 用户登录 </h2>
</div>
<div class="form-group">
<label for="username" class="col-sm-offset-3 col-sm-2 control-label"> 用户名:</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="username" name="username" placeholder="请输出用户名" />
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-offset-3 col-sm-2 control-label"> 明码:</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="password" name="password" placeholder="请输出明码" />
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-5 col-sm-4">
<button type="submit" class="btn btn-default"> 登录 </button>
</div>
</div>
</form>
</div>
<script src="/js/jquery-2.2.1.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>
2、写一个 LoginController
package com.zb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {@RequestMapping("/login")
public String login(){return "login";}
}
3、加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
4、
重启,http://localhost:8080/login
则会看到登录页面
四、集成 mybatis
1、
springboot 的配置文件分为两种:application.properties 和 application.yml
咱们把它改成 application.yml 这种更直观
spring:
application:
name: myspringboot
output:
ansi:
enabled: always
profiles:
active: dev
thymeleaf:
encoding: UTF-8
prefix: classpath:/templates/
server:
tomcat:
uri-encoding: UTF-8
max-connections: 500
min-spare-threads: 25
max-threads: 300
accept-count: 200
port: 8080
mybatis:
type-aliases-package: com.zb.mapper
mapper-locations: classpath:mapping/*.xml
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
logging:
level:
com.zb.mapper: debug
---
#开发配置
spring:
profiles: dev
datasource:
url: jdbc:mysql://localhost:3306/zb_db?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
2、增加依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>RELEASE</version>
</dependency>
<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
3、建表
CREATE datebase zb_db;
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`mobile` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`sex` varchar(255) DEFAULT NULL,
`nickname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(1, 'admin', '123456', '13918891675','mmc@163.com', '男', '管理员');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(2, 'lisi2', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(3, 'lisi3', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(4, 'lisi4', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(5, 'lisi5', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(6, 'lisi6', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(7, 'lisi7', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(8, 'lisi8', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(9, 'lisi9', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(10, 'lisi10', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(11, 'lisi11', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(12, 'lisi12', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(13, 'lisi13', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
insert into `user`(id, username, password, mobile, email,sex, nickname) VALUES(14, 'lisi14', '123456', '13918891675','mmc@163.com', 'm', 'lisi1');
4、用 mybatisgenerator 主动生成文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库驱动: 抉择你的本地硬盘下面的数据库驱动包 -->
<classPathEntry location="C:\Users\DELL\Downloads\mysql-connector-java-5.1.22-bin.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<!-- 是否去除主动生成的正文 true:是:false: 否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- 数据库链接 URL,用户名、明码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1/zb_db" userId="root" password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 生成模型的包名和地位 -->
<javaModelGenerator targetPackage="com.zb.model" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成映射文件的包名和地位 -->
<sqlMapGenerator targetPackage="mapping" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 生成 DAO 的包名和地位 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.zb.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 要生成的表 tableName 是数据库中的表名或视图名 domainObjectName 是实体类名 -->
<table tableName="role_permission" domainObjectName="RolePermission" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
5、在 pom 外面增加 plugin
<!-- mybatis generator 主动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
</plugin>
6、用 mvn mybatis-generator:generate - e 命令生成文件
此时目录构造
7、写 dao 和 service,controller,mapper
mapper 减少了几个办法
@Select("Select * from user")
List<User> selectAll();
@Select("Select * from user where username = #{username} and password = #{password}")
User selectByUsernamePass(@Param("username") String username, @Param("password") String password);
@Select("Select * from user where username = #{username}")
User selectByUsername(@Param("username") String username);
dao 和 service 都是失常调用,上面是 controller
package com.zb.controller;
import com.github.pagehelper.PageInfo;
import com.zb.model.User;
import com.zb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/user")
@ResponseBody
public User getUserById(int id){User user = userService.selectByPrimaryKey(id);
return user;
}
@RequestMapping("/userlist")
public String getUserList(Model model, PageInfo pageInfo){int pageNum = (pageInfo.getPageNum() == 0)? 1 : pageInfo.getPageNum();
int pageSize = (pageInfo.getPageSize() == 0)? 10 : pageInfo.getPageSize();
PageInfo<User> result = userService.selectAll(pageNum, pageSize);
model.addAttribute("users", result.getList());
model.addAttribute("pageInfo", result);
return "userlist";
}
@RequestMapping("/userdelete")
public String userdelete(int id){userService.deleteByPrimaryKey(id);
return "redirect:/userlist";
}
@RequestMapping("/useredit")
public String useredit(int id, Model model){User user = userService.selectByPrimaryKey(id);
model.addAttribute("user", user);
return "useredit";
}
@RequestMapping(value = "/userupdateoradd", method = RequestMethod.POST)
public String userUpdateOrAdd(User user){if(user.getId() == 0){userService.insertSelective(user);
} else {userService.updateByPrimaryKeySelective(user);
}
return "redirect:/userlist";
}
}
页面 userlist.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title> 用户治理 </title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div>
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div>
<ul class="nav navbar-nav" >
<li><a href="/userlist"> 用户治理 </a></li>
<li><a href="#"> 书籍治理 </a></li>
</ul>
</div>
</div>
</nav>
</div>
<div>
<h2> 用户治理 </h2>
<table width="100%" border="0" cellpadding="0" cellspacing="0" class="table_list">
<thead>
<tr>
<th width="20%"> 编号 </th>
<th width="20%"> 用户名 </th>
<th width="20%"> 电子邮箱 </th>
<th width="20%"> 手机 </th>
<th width="20%"> 操作 </th>
</tr>
</thead>
<tbody>
<tr th:each="user:${users}">
<td height="40px"><a th:text="${user.id}" data-toggle="modal" data-target="#myModal" onclick="values(this)"></a></td>
<td th:text="${user.username}"></td>
<td th:text="${user.email}"></td>
<td th:text="${user.mobile}"></td>
<td><a href="#" class="delete_a" th:value="${user.id}"> 删除 </a></td>
</tr>
</tbody>
</table>
</div>
</div>
<script src="/js/jquery-2.2.1.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>
8,在 ZbookApplication 上加上注解扫描
@ComponentScan(basePackages = {"com.zb"})
@MapperScan("com.zb.mapper")
9、顺便把分页加上(依赖包之前曾经加了)
service 层
@Override
public PageInfo<User> selectAll(int pageNum, int pageSize) {PageHelper.startPage(pageNum, pageSize);
List<User> users = userDao.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users);
return pageInfo;
}
controller 层
public String getUserList(Model model, PageInfo pageInfo){int pageNum = (pageInfo.getPageNum() == 0)? 1 : pageInfo.getPageNum();
int pageSize = (pageInfo.getPageSize() == 0)? 10 : pageInfo.getPageSize();
PageInfo<User> result = userService.selectAll(pageNum, pageSize);
model.addAttribute("users", result.getList());
model.addAttribute("pageInfo", result);
return "userlist";
}
页面批改:
<div id="example" style="text-align: center"> <ul id="pageLimit"></ul> </div>
<input type="hidden" id="pageNum" name="pageNum" th:value="${pageInfo.pageNum}" />
<input type="hidden" id="pages" name="pages" th:value="${pageInfo.pages}" />
<script src="/js/bootstrap-paginator.min.js"></script>
<script>
$('#pageLimit').bootstrapPaginator({currentPage: $("#pageNum").val(),
totalPages: $("#pages").val(),
size: "normal",
bootstrapMajorVersion: 3,
alignment: "right",
numberOfPages: 5,
itemTexts: function (type, page, current) {switch (type) {
case "first": return "首页";
case "prev": return "上一页";
case "next": return "下一页";
case "last": return "末页";
case "page": return page;
}
},
onPageClicked: function (event, originalEvent, type, page){// 给每个页眉绑定一个事件,其实就是 ajax 申请,其中 page 变量为以后点击的页上的数字。window.location.href = "userlist?pageNum=" + page;
}
});
$(function(){$(".delete_a").click(function(){var userId=$(this).attr("value");
if(confirm("确认删除吗?")){
window.location.href="/userdelete?id=" + userId;
return ;
}
});
});
</script>
此时目录
此时重启,输出 http://localhost:8080/userlist
就会看到 user 列表,也能够分页。
五、简略登录,用 filter 实现
1、
package com.zb.filter;
import com.zb.model.User;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(filterName = "sessionFilter",urlPatterns = {"/*"})
public class SessionFilter implements Filter {
String NO_LOGIN = "您还未登录";
String[] includeUrls = new String[]{"/login","/login_in"};
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
HttpSession session = request.getSession();
String url = request.getRequestURI();
boolean needFilter = isNeedFilter(url);
// 动态资源放行
if(url.endsWith(".css")||url.endsWith(".js")||url.endsWith(".jpg")
||url.endsWith(".gif")||url.endsWith(".png")){filterChain.doFilter(servletRequest, servletResponse);
return;
}
if(!needFilter){filterChain.doFilter(servletRequest, servletResponse);
} else {User user = (User)session.getAttribute(session.getId());
if(user != null){filterChain.doFilter(servletRequest, servletResponse);
} else {String requestType = request.getHeader("X-Requested-With");
// 判断是否是 ajax 申请
if(requestType!=null && "XMLHttpRequest".equals(requestType)){response.getWriter().write(this.NO_LOGIN);
}else{// 重定向到登录页(须要在 static 文件夹下建设此 html 文件)
response.sendRedirect(request.getContextPath()+"/login");
}
return;
}
}
}
public boolean isNeedFilter(String uri) {for (String includeUrl : includeUrls) {if(includeUrl.equals(uri)) {return false;}
}
return true;
}
@Override
public void destroy() {}
}
2、在 ZbookApplication 上加注解
@ServletComponentScan
3、在 LoginController 下写登录逻辑
package com.zb.controller;
import com.zb.model.User;
import com.zb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public String login(){return "login";}
@RequestMapping(value = "/login_in", method = RequestMethod.POST)
public String login_in(User user, HttpServletRequest request, Model model){User user1 = userService.validateUser(user.getUsername(), user.getPassword());
if(user1 == null){return "login";}
HttpSession session = request.getSession();
session.setAttribute(session.getId(), user1);
return "redirect:/userlist";
}
@RequestMapping("/logout")
public String logout(HttpServletRequest request){request.getSession().removeAttribute(request.getSession().getId());
return "login";
}
}
当初就能够简略的登录了
4、批改页面让页面显示用户名和退出
<div>
<a href="logout" style="display: inline-block; float: right"> 退出 </a>
<p th:text="${#httpSession.getAttribute(#httpSession.getId()).username}" style="display: inline-block; float: right"></p>
<p style="display: inline-block; float: right"> 您好,</p>
</div>
六、权限
权限治理咱们应用当初比拟风行的 shiro, 原理就不说了,间接说怎么应用
1、加依赖包
<!--shiro 权限 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2、建表
shiro 须要 5 张表:用户、角色、权限、用户角色关联表,角色权限关联表
用户表已建设,当初续建 4 张表
CREATE TABLE `role` (`id` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`status` varchar(255) DEFAULT NULL,
`create_time` DATE DEFAULT NULL,
`update_time` DATE DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `permission` (`id` int(11) NOT NULL AUTO_INCREMENT,
`permissionname` varchar(255) DEFAULT NULL,
`resourceType` varchar(255) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`permission` varchar(255) DEFAULT NULL,
`status` varchar(255) DEFAULT NULL,
`create_time` DATE DEFAULT NULL,
`update_time` DATE DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `user_role` (`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`role_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `role_permission` (`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` varchar(255) DEFAULT NULL,
`permission_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
insert into `role`(id, rolename, description, status, create_time, update_time) VALUES (1, 'admin', '管理员', 'use', '2018-08-10', '2018-08-10');
insert into `role`(id, rolename, description, status, create_time, update_time) VALUES (2, 'manage', '经理', 'use', '2018-08-10', '2018-08-10');
insert into `role`(id, rolename, description, status, create_time, update_time) VALUES (3, 'user', '普通用户', 'use', '2018-08-10', '2018-08-10');
INSERT INTO `permission` (id, permissionname, resourceType, url, permission, status, create_time, update_time) VALUES (1,'用户治理','menu', 'userlist','user:list','use','2018-08-10', '2018-08-10');
INSERT INTO `permission` (id, permissionname, resourceType, url, permission, status, create_time, update_time) VALUES (2,'用户批改','menu', 'useredit','user:edit','use','2018-08-10', '2018-08-10');
INSERT INTO `permission` (id, permissionname, resourceType, url, permission, status, create_time, update_time) VALUES (3,'用户删除','menu', 'userdelete','user:delete','use','2018-08-10', '2018-08-10');
INSERT INTO `user_role` (id, user_id, role_id) VALUES (1, 1 ,1);
INSERT INTO `user_role` (id, user_id, role_id) VALUES (2, 1 ,2);
INSERT INTO `user_role` (id, user_id, role_id) VALUES (3, 1 ,3);
INSERT INTO `user_role` (id, user_id, role_id) VALUES (4, 2 ,2);
INSERT INTO `user_role` (id, user_id, role_id) VALUES (5, 3 ,3);
INSERT INTO `user_role` (id, user_id, role_id) VALUES (6, 4 ,3);
INSERT INTO `role_permission` (id, role_id, permission_id) VALUES (1, 1, 1);
INSERT INTO `role_permission` (id, role_id, permission_id) VALUES (2, 1, 2);
INSERT INTO `role_permission` (id, role_id, permission_id) VALUES (3, 1, 3);
INSERT INTO `role_permission` (id, role_id, permission_id) VALUES (4, 2, 1);
INSERT INTO `role_permission` (id, role_id, permission_id) VALUES (5, 2, 2);
INSERT INTO `role_permission` (id, role_id, permission_id) VALUES (6, 3, 1);
3、加载 bean
package com.zb.shiro;
import java.util.LinkedHashMap;
import java.util.Map;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
/**
* Shiro 配置
*
Apache Shiro 外围通过 Filter 来实现,就如同 SpringMvc 通过 DispachServlet 来主控制一样。既然是应用 Filter 个别也就能猜到,是通过 URL 规定来进行过滤和权限校验,所以咱们须要定义一系列对于 URL 的规定和拜访权限。*/
@Configuration
public class ShiroConfiguration {private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
@Bean
public FilterRegistrationBean delegatingFilterProxy(){FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter");
filterRegistrationBean.setFilter(proxy);
return filterRegistrationBean;
}
/**
* ShiroFilterFactoryBean 解决拦挡资源文件问题。* 留神:独自一个 ShiroFilterFactoryBean 配置是或报错的,认为在
* 初始化 ShiroFilterFactoryBean 的时候须要注入:SecurityManager
*
Filter Chain 定义阐明
1、一个 URL 能够配置多个 Filter,应用逗号分隔
2、当设置多个过滤器时,全副验证通过,才视为通过
3、局部过滤器可指定参数,如 perms,roles
*
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){logger.info("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置退出过滤器, 其中的具体的退出代码 Shiro 曾经替咱们实现了
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/*/*.js", "anon");
filterChainDefinitionMap.put("/*/*.css", "anon");
filterChainDefinitionMap.put("/login_in", "anon");
filterChainDefinitionMap.put("/login", "anon");
//<!-- 过滤链定义,从上向下程序执行,个别将 /** 放在最为下边 -->: 这是一个坑呢,一不小心代码就不好使了;
//<!-- authc: 所有 url 都必须认证通过才能够拜访; anon: 所有 url 都都能够匿名拜访 -->
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会主动寻找 Web 工程根目录下的 "/login.jsp" 页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录胜利后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/userlist");
// 未受权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
@Bean
public MyShiroRealm myShiroRealm(){MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
/**
* 开启 Shiro 的注解(如 @RequiresRoles,@RequiresPermissions), 需借助 SpringAOP 扫描应用 Shiro 注解的类, 并在必要时进行平安逻辑验证
* 配置以下两个 bean(DefaultAdvisorAutoProxyCreator 和 AuthorizationAttributeSourceAdvisor)即可实现此性能
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启 aop 注解反对
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public ShiroDialect shiroDialect() {return new ShiroDialect();
}
}
4、写 Realm
package com.zb.shiro;
import com.zb.model.Permission;
import com.zb.model.Role;
import com.zb.model.User;
import com.zb.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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
/**
* 身份校验外围类;
*
* @version v.0.1
*/
public class MyShiroRealm extends AuthorizingRealm{private static final Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);
private static String SALT = "mySlalt";
@Resource
private UserService userService;
/**
* 认证信息.(身份验证)
* :
* Authentication 是用来验证用户身份
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {logger.info("MyShiroRealm.doGetAuthenticationInfo()");
// 获取用户的输出的账号.
String username = (String)token.getPrincipal();
// 通过 username 从数据库中查找 User 对象,如果找到,没找到.
// 理论我的项目中,这里能够依据理论状况做缓存,如果不做,Shiro 本人也是有工夫距离机制,2 分钟内不会反复执行该办法
User user = userService.selectByUsername(username);
logger.info("----->>userInfo=" + user.toString());
if(user == null){return null;}
/*
* 获取权限信息: 这里没有进行实现,* 请自行依据 UserInfo,Role,Permission 进行实现;* 获取之后能够在前端 for 循环显示所有链接;
*/
//userInfo.setPermissions(userService.findPermissions(user));
userService.findRoleAndPermissions(user);
// 账号判断;
// 加密形式;
// 交给 AuthenticatingRealm 应用 CredentialsMatcher 进行明码匹配,如果感觉人家的不好能够自定义实现
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user, // 用户名
user.getPassword(), // 明码
ByteSource.Util.bytes(user.getUsername() + SALT),//salt=username+salt
getName() //realm name);
// 明文: 若存在,将此用户寄存到登录认证 info 中,无需本人做明码比照,Shiro 会为咱们进行明码比照校验
// SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
// userInfo, // 用户名
// userInfo.getPassword(), // 明码
// getName() //realm name
// );
return authenticationInfo;
}
/**
* 此办法调用 hasRole,hasPermission 的时候才会进行回调.
*
* 权限信息.(受权):
* 1、如果用户失常退出,缓存主动清空;* 2、如果用户非正常退出,缓存主动清空;* 3、如果咱们批改了用户的权限,而用户不退出零碎,批改的权限无奈立刻失效。*(须要手动编程进行实现;放在 service 进行调用)* 在权限批改后调用 realm 中的办法,realm 曾经由 spring 治理,所以从 spring 中获取 realm 实例,* 调用 clearCached 办法;* :Authorization 是受权访问控制,用于对用户进行的操作受权,证实该用户是否容许进行以后操作,如拜访某个链接,某个资源文件等。* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {logger.info("权限配置 -->MyShiroRealm.doGetAuthorizationInfo()");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User userInfo = (User)principals.getPrimaryPrincipal();
/// 在认证胜利之后返回.
// 设置角色信息.
// 反对 Set 汇合,
// 用户的角色对应的所有权限
for(Role role:userInfo.getRoleList()){authorizationInfo.addRole(role.getRolename());
for(Permission p:role.getPermissionList()){authorizationInfo.addStringPermission(p.getPermission());
}
}
return authorizationInfo;
}
}
5、主动生成那四个表的代码,加相干的
@Select("select * from Role where id in (select role_id from user_role where user_id = #{userId})")
List<Role> selectRoleByUserId(@Param("userId") int userId);
@Select("select * from permission where id in (select permission_id from role_permission where role_id = #{roleId})")
List<Permission> selectPermissionIdByRoleId(@Param("roleId") int roleId);
6、批改 LoginController
批改登录办法
@RequestMapping(value = "/login_in", method = RequestMethod.POST)
public String login_in(User user, HttpServletRequest request, Model model){User user1 = userService.validateUser(user.getUsername(), user.getPassword());
if(user1 == null){return "login";}
// (1) session
// HttpSession session = request.getSession();
// session.setAttribute(session.getId(), user1);
// return "redirect:/userlist";
// (3) shiro
String msg ;
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
token.setRememberMe(true);
Subject subject = SecurityUtils.getSubject();
try {subject.login(token);
if (subject.isAuthenticated()) {
//shiro 的 session 和 request 的 session 封装的是一个,就是说两个都能够
// request.getSession().setAttribute(request.getSession().getId(),user);
subject.getSession().setAttribute(subject.getSession().getId(),user);
return "redirect:/userlist";
} else {return "login";}
} catch (IncorrectCredentialsException e) {msg = "登录明码谬误. Password for account" + token.getPrincipal() + "was incorrect.";
model.addAttribute("message", msg);
System.out.println(msg);
} catch (ExcessiveAttemptsException e) {
msg = "登录失败次数过多";
model.addAttribute("message", msg);
System.out.println(msg);
} catch (LockedAccountException e) {msg = "帐号已被锁定. The account for username" + token.getPrincipal() + "was locked.";
model.addAttribute("message", msg);
System.out.println(msg);
} catch (DisabledAccountException e) {msg = "帐号已被禁用. The account for username" + token.getPrincipal() + "was disabled.";
model.addAttribute("message", msg);
System.out.println(msg);
} catch (ExpiredCredentialsException e) {msg = "帐号已过期. the account for username" + token.getPrincipal() + "was expired.";
model.addAttribute("message", msg);
System.out.println(msg);
} catch (UnknownAccountException e) {msg = "帐号不存在. There is no user with username of" + token.getPrincipal();
model.addAttribute("message", msg);
System.out.println(msg);
} catch (UnauthorizedException e) {msg = "您没有失去相应的受权!" + e.getMessage();
model.addAttribute("message", msg);
System.out.println(msg);
}
return "login";
}
@RequestMapping("/logout")
public String logout(HttpServletRequest request){// request.getSession().removeAttribute(request.getSession().getId());
SecurityUtils.getSubject().getSession().removeAttribute(SecurityUtils.getSubject().getSession().getId());
return "login";
}
7、批改 contorller 办法,或者在页面外面加权限
@RequestMapping("/userlist")
@RequiresPermissions("user:list")
@RequestMapping("/userdelete")
@RequiresPermissions("user:delete")
这种或者
页面上
<shiro:hasPermission name="user:delete"></shiro:hasPermission>
至此,权限就加好了
欢送关注微信公众号:丰极,回复:springboot1,有我的项目的 github 地址。