Spring Boot学习
官网:https://spring.io/projects/spring-boot#overview
文档:https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/
参考视频:【狂神说Java】SpringBoot最新教程IDEA版通俗易懂_哔哩哔哩_bilibili
项目完整参考代码:lexiaoyuan/SpringBootStudy: My Spring Boot study notes (github.com)、SpringBootStudy: 我的Spring Boot学习笔记 (gitee.com)
【补充】安装Easy Code插件
- 在设置中的插件市场,搜索Easy Code,点击安装,安装完成后重启IDEA即可。(我这里已经安装过了)
整合MyBatis
-
首先,打开mysql,确保idea能连接上。
-
在
pom.xml
中导入一些依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
- 使用Easy Code插件生成代码,按下图操作即可
- 生成代码后的目录,其中
UserServiceImpl.java
的目录自己做了调整
- 配置一下数据源和MyBatis,在resources目录下新建一个
application.yml
文件
# 配置数据源
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# MyBatis配置
mybatis:
type-aliases-package: com.springboot.entity
mapper-locations: classpath:mapper/*.xml
- 在
User.java
中增加一个toString方法
/**
* (User)实体类
*/
public class User{
private Integer id;
private String name;
private String pwd;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- 在
UserDao.java
中增加一些代码
/**
* (User)表数据库访问层
*/
@Repository
@Mapper
public interface UserDao {
/**
* 通过ID查询单条数据
*
* @param id 主键
* @return 实例对象
*/
User queryById(Integer id);
User queryByName(String name);
/**
* 查询指定行数据
*
* @param offset 查询起始位置
* @param limit 查询条数
* @return 对象列表
*/
List<User> queryAllByLimit(@Param("offset") int offset, @Param("limit") int limit);
/**
* 通过实体作为筛选条件查询
*
* @param user 实例对象
* @return 对象列表
*/
List<User> queryAll(User user);
/**
* 新增数据
*
* @param user 实例对象
* @return 影响行数
*/
int insert(User user);
/**
* 修改数据
*
* @param user 实例对象
* @return 影响行数
*/
int update(User user);
/**
* 通过主键删除数据
*
* @param id 主键
* @return 影响行数
*/
int deleteById(Integer id);
}
- 在
UserService.java
中也增加一个queryByName
方法
/**
* (User)表服务接口
*/
public interface UserService {
/**
* 通过ID查询单条数据
*
* @param id 主键
* @return 实例对象
*/
User queryById(Integer id);
User queryByName(String name);
/**
* 查询多条数据
*
* @param offset 查询起始位置
* @param limit 查询条数
* @return 对象列表
*/
List<User> queryAllByLimit(int offset, int limit);
/**
* 新增数据
*
* @param user 实例对象
* @return 实例对象
*/
User insert(User user);
/**
* 修改数据
*
* @param user 实例对象
* @return 实例对象
*/
User update(User user);
/**
* 通过主键删除数据
*
* @param id 主键
* @return 是否成功
*/
boolean deleteById(Integer id);
}
- 在
UserServiceImpl.java
中也增加一个queryByName
方法
/**
* (User)表服务实现类
*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
/**
* 通过ID查询单条数据
*
* @param id 主键
* @return 实例对象
*/
@Override
public User queryById(Integer id) {
return this.userDao.queryById(id);
}
@Override
public User queryByName(String name) {
return this.userDao.queryByName(name);
}
/**
* 查询多条数据
*
* @param offset 查询起始位置
* @param limit 查询条数
* @return 对象列表
*/
@Override
public List<User> queryAllByLimit(int offset, int limit) {
return this.userDao.queryAllByLimit(offset, limit);
}
/**
* 新增数据
*
* @param user 实例对象
* @return 实例对象
*/
@Override
public User insert(User user) {
this.userDao.insert(user);
return user;
}
/**
* 修改数据
*
* @param user 实例对象
* @return 实例对象
*/
@Override
public User update(User user) {
this.userDao.update(user);
return this.queryById(user.getId());
}
/**
* 通过主键删除数据
*
* @param id 主键
* @return 是否成功
*/
@Override
public boolean deleteById(Integer id) {
return this.userDao.deleteById(id) > 0;
}
}
- 在
UserDao.xml
中也增加一个queryByName
的SQL语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springboot.dao.UserDao">
<resultMap type="com.springboot.entity.User" id="UserMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="pwd" column="pwd" jdbcType="VARCHAR"/>
</resultMap>
<!--查询单个-->
<select id="queryById" resultMap="UserMap">
select
id, name, pwd
from mybatis.user
where id = #{id}
</select>
<select id="queryByName" parameterType="String" resultType="user">
select
id, name, pwd
from mybatis.user
where name = #{name}
</select>
<!--查询指定行数据-->
<select id="queryAllByLimit" resultMap="UserMap">
select
id, name, pwd
from mybatis.user
limit #{offset}, #{limit}
</select>
<!--通过实体作为筛选条件查询-->
<select id="queryAll" resultMap="UserMap">
select
id, name, pwd
from mybatis.user
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="name != null and name != ''">
and name = #{name}
</if>
<if test="pwd != null and pwd != ''">
and pwd = #{pwd}
</if>
</where>
</select>
<!--新增所有列-->
<insert id="insert" keyProperty="id" useGeneratedKeys="true">
insert into mybatis.user(name, pwd)
values (#{name}, #{pwd})
</insert>
<!--通过主键修改数据-->
<update id="update">
update mybatis.user
<set>
<if test="name != null and name != ''">
name = #{name},
</if>
<if test="pwd != null and pwd != ''">
pwd = #{pwd},
</if>
</set>
where id = #{id}
</update>
<!--通过主键删除-->
<delete id="deleteById">
delete from mybatis.user where id = #{id}
</delete>
</mapper>
- 首先在测试类里测试一下
@SpringBootTest
class Springboot08ShiroApplicationTests {
@Autowired
private UserServiceImpl userService;
@Test
void contextLoads() {
System.out.println(userService.queryById(1).toString());
}
}
- 运行测试类,可以看到,没有问题。
- 修改
UserRealm.java
里的认证方法,通过数据库查询用户
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了===》认证doGetAuthorizationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
// 用户名、密码认证,从数据库取
User user = userService.queryByName(userToken.getUsername());
if (user == null) // 没有该用户(名)
return null; // 会抛出异常 UnknownAccountException
// 密码认证,shiro做
// 可以加密:MD5、MD5盐值加密
return new SimpleAuthenticationInfo("",user.getPwd(),"");
}
- 再运行web项目,访问:http://localhost:8080/toLogin,输入错误的用户名和密码依然可以提示相关信息,同时,输入数据库中user表中的用户名和密码,则可以登录成功
- OK,整合MyBatis和Druid完成!
请求授权
- 在
ShiroConfig.java
中的方法里添加授权规则
// 3. 创建ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro的内置过滤器
/**
* anon: 无需认证就可以访问
* authc: 必须认证了才能访问
* user: 必须有记住我功能才能访问
* perms: 拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
// 登录拦截
Map<String, String> filterMap = new LinkedHashMap<>();
// 授权 (要放在前面)
filterMap.put("/user/add", "perms[user:add]"); // 有add权限才能访问add页面
filterMap.put("/user/update", "perms[user:update]"); // 有update权限才能访问update页面
// 拦截请求
filterMap.put("/user/*", "authc"); // 支持通配符
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 设置登录请求
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
- 测试:运行一下项目,访问:http://localhost:8080/toLogin,输入正确的用户名和密码(比如:乐小猿,admin)后,登录,再点击add或update链接,发现未授权,
- 配置未授权的页面:
- 在
MyController.java
中增加一个方法
@GetMapping("/unauthorized")
@ResponseBody
public String unauthorized() {
return "没有权限访问该页面";
}
- 在上面设置登录请求之后再设置未授权的页面
// 设置未授权的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
- 重复上面的测试,可以看到,进入了自定义的未授权的页面
- 给用户添加权限:在
UserRealm.java
中的授权方法中进行授权
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了===》授权doGetAuthorizationInfo");
// 进入这个方法就添加权限给用户
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addStringPermission("user:add");
return authorizationInfo;
}
- 重复上面的测试,可以看到,登录成功后可以访问add页面
- 通过数据库得到当前用户的权限:
- 首先修改一下user表,增加一列
- 然后用Easy Code重新生成一下实体类
User.java
(自己加了个toString方法)和UserDao.xml
(自己加了个queryByName的SQL语句)
/**
* (User)实体类
*/
public class User implements Serializable {
private static final long serialVersionUID = 227972241159940478L;
private Integer id;
private String name;
private String pwd;
private String perms;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getPerms() {
return perms;
}
public void setPerms(String perms) {
this.perms = perms;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
", perms='" + perms + '\'' +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springboot.dao.UserDao">
<resultMap type="com.springboot.entity.User" id="UserMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="pwd" column="pwd" jdbcType="VARCHAR"/>
<result property="perms" column="perms" jdbcType="VARCHAR"/>
</resultMap>
<!--查询单个-->
<select id="queryById" resultMap="UserMap">
select
id, name, pwd, perms
from mybatis.user
where id = #{id}
</select>
<select id="queryByName" parameterType="String" resultType="user">
select
id, name, pwd, perms
from mybatis.user
where name = #{name}
</select>
<!--查询指定行数据-->
<select id="queryAllByLimit" resultMap="UserMap">
select
id, name, pwd, perms
from mybatis.user
limit #{offset}, #{limit}
</select>
<!--通过实体作为筛选条件查询-->
<select id="queryAll" resultMap="UserMap">
select
id, name, pwd, perms
from mybatis.user
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="name != null and name != ''">
and name = #{name}
</if>
<if test="pwd != null and pwd != ''">
and pwd = #{pwd}
</if>
<if test="perms != null and perms != ''">
and perms = #{perms}
</if>
</where>
</select>
<!--新增所有列-->
<insert id="insert" keyProperty="id" useGeneratedKeys="true">
insert into mybatis.user(name, pwd, perms)
values (#{name}, #{pwd}, #{perms})
</insert>
<!--通过主键修改数据-->
<update id="update">
update mybatis.user
<set>
<if test="name != null and name != ''">
name = #{name},
</if>
<if test="pwd != null and pwd != ''">
pwd = #{pwd},
</if>
<if test="perms != null and perms != ''">
perms = #{perms},
</if>
</set>
where id = #{id}
</update>
<!--通过主键删除-->
<delete id="deleteById">
delete from mybatis.user where id = #{id}
</delete>
</mapper>
- 修改一下
UserRealm.java
中的认证和授权的方法
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了===》授权doGetAuthorizationInfo");
// 进入这个方法就添加权限给用户
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//authorizationInfo.addStringPermission("user:add");
// 拿到当前的对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal(); // 拿到下面认证方法中传上来的user对象
// 添加当前用户的权限,通过数据库得到当前用户的权限
authorizationInfo.addStringPermission(currentUser.getPerms());
return authorizationInfo;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了===》认证doGetAuthorizationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
// 用户名、密码认证,从数据库取
User user = userService.queryByName(userToken.getUsername());
if (user == null) // 没有该用户(名)
return null; // 会抛出异常 UnknownAccountException
// 把从数据库中查到的user传递到上面授权的方法
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
- 测试:访问:http://localhost:8080/toLogin,使用数据库中user表中的用户名和密码登录,可以看到,只有个拥有一定权限的用户才能访问其能访问的页面。测试OK!
- 请求授权完成!
shiro整合thymeleaf
官网文档:https://github.com/theborakompanioni/thymeleaf-extras-shiro
- 在
pom.xml
中添加整合依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
- 在
ShiroConfig.java
中整合shiro和thymeleaf
// 整合ShiroDialect:用来整合shiro和thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
- 在
index.html
中添加判断
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<p shiro:notAuthenticated="">
<a th:href="@{/toLogin}">登录</a>
</p>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
- 重新运行项目,访问:http://localhost:8080/,没有登录时只显示登录
- 点击登录,用乐小猿的账户登录,只有add权限,就只显示add页面
- 用beta的账户登录,只有update权限,就只显示update页面
- OK,整合完成!