新建一个springBoot项目
- 使用IDEA新建一个项目
创建的项目结构如下
- 配置
application.yml
server:
# 服务器的HTTP端口,默认为80 need
port: 8081
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
- 创建一个控制类
package com.boo.spring_boot_demo.web.controller.test;
@Controller
@RequestMapping("/test")
public class TestController {
private final String prefix = "test/";
// 返回json数据
@PostMapping("/test")
@ResponseBody
public String test(){
return "test";
}
// 返回一个页面
@GetMapping("/hello")
public String hello(){
return prefix + "hello";
}
}
3.1 测试访问服务
3.1.1 启动项目,访问/test/test
(这里用的postman),如图:
3.1.2 访问/test/hello
如图,访问报404错误
原因:没有配置后台模板引擎
- 配置后台模板引擎,这里用的是
thymeleaf
4.1 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
4.2 在application.yml
中添加模板引擎相关配置
# spring 配置
spring:
# 模板引擎 need
thymeleaf:
prefix:
classpath: /templates # 访问template下的html文件需要配置模板,映射
# 禁用缓存
cache: false
4.3 重启服务后访问/test/hello
5. 在项目中使用日志
springBoot web默认支持 slf4j
,在项目中的使用
5.1 代码声明
private static final Logger log = LoggerFactory.getLogger(SysLoginController.class);
5.2 注解方式
使用
log.error();
log.warn();
log.info();
...
整合shiro
shiro 主要有三大功能模块:
1. Subject:主体,一般指用户。
2. SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
3. Realms:用于进行权限信息的验证,一般需要自己实现
- 引入相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.7.0</version>
</dependency>
- 创建权限控制相关实体类(参考)
2.1 用户实体类
2.2 角色@Data @AllArgsConstructor // 添加一个构造函数,该构造函数含有所有已声明字段属性参数 public class SysUser { private String id; private String userName; private String password; /** 用户对应的角色集合 */ private Set<SysRole> roles; }
2.3 权限@Data @AllArgsConstructor public class SysRole { private String id; private String roleName; /** 角色对应权限集合 */ private Set<SysPermissions> permissions; }
@Data @AllArgsConstructor public class SysPermissions { private String id; private String permissionsName; }
- 用户信息管理(模拟查询用户数据)
@Service
public class SysLoginServiceImpl implements ISysLoginService {
@Override
public SysUser getSysUserByUserName(String getMapByName) {
return getUserByUserName(getMapByName);
}
/**
* 模拟数据库查询
* @param userName 用户名
* @return SysUser
*/
private SysUser getUserByUserName(String userName) {
SysPermissions permissions1 = new SysPermissions("1", "data:query");
SysPermissions permissions2 = new SysPermissions("2", "data:add");
Set<SysPermissions> permissionsSet = new HashSet<>();
permissionsSet.add(permissions1);
permissionsSet.add(permissions2);
SysRole role = new SysRole("1", "admin", permissionsSet);
Set<SysRole> roleSet = new HashSet<>();
roleSet.add(role);
SysUser user = new SysUser("1", "boo", "123456", roleSet);
Map<String, SysUser> map = new HashMap<>();
map.put(user.getUserName(), user);
Set<SysPermissions> permissionsSet1 = new HashSet<>();
permissionsSet1.add(permissions1);
SysRole role1 = new SysRole("2", "user", permissionsSet1);
Set<SysRole> roleSet1 = new HashSet<>();
roleSet1.add(role1);
SysUser user1 = new SysUser("2", "wangwu", "123456", roleSet1);
map.put(user1.getUserName(), user1);
return map.get(userName);
}
}
- 自定义Realm
用于查询用户的角色和权限信息并保存到权限管理器
package com.boo.spring_boot_demo.framework.shiro.realm;
import cn.hutool.core.util.StrUtil;
import com.boo.spring_boot_demo.domain.system.SysPermissions;
import com.boo.spring_boot_demo.domain.system.SysRole;
import com.boo.spring_boot_demo.domain.system.SysUser;
import com.boo.spring_boot_demo.service.system.ISysLoginService;
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.springframework.beans.factory.annotation.Autowired;
/** 自定义Realm用于查询用户的角色和权限信息并保存到权限管理器
* description: CustomRealm <br>
* date: 2021/3/23 11:24 <br>
* author: boo <br>
* version: 1.0 <br>
*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
private ISysLoginService loginService;
/** 功能描述: 权限配置
* @param principalCollection
* @return: org.apache.shiro.authz.AuthorizationInfo
* @Author: boo
* @Date: 2021/3/23 11:25
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
//查询用户名称
SysUser user = loginService.getSysUserByUserName(name);
//添加角色和权限 SimpleAuthorizationInfo: 授权信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (SysRole role : user.getRoles()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
//添加权限
for (SysPermissions permissions : role.getPermissions()) {
simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
}
}
return simpleAuthorizationInfo;
}
/** 功能描述: 认证配置
* @param authenticationToken 认证令牌
* @return: org.apache.shiro.authc.AuthenticationInfo
* @Author: boo
* @Date: 2021/3/23 11:25
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if (StrUtil.isEmptyIfStr(authenticationToken.getPrincipal())) {
return null;
}
//获取用户信息
String name = authenticationToken.getPrincipal().toString();
SysUser user = loginService.getSysUserByUserName(name);
if (user == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
return simpleAuthenticationInfo;
}
}
}
- ShiroConfig配置类
把CustomRealm和SecurityManager等注入到spring容器中
package com.boo.spring_boot_demo.framework.config;
import com.boo.spring_boot_demo.framework.shiro.realm.CustomRealm;
import org.apache.shiro.mgt.SessionsSecurityManager;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* description: ShiroConfig <br>
* date: 2021/3/23 11:39 <br>
* author: boo <br>
* version: 1.0 <br>
*/
@Configuration
public class ShiroConfig {
@Bean
@ConditionalOnMissingBean // 基于@bean 动态加载bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//将自己的验证方式加入容器
@Bean
public CustomRealm myShiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public SessionsSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SessionsSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
//登出
map.put("/logout", "logout");
//对所有用户认证
map.put("/**", "authc");
//登录
shiroFilterFactoryBean.setLoginUrl("/login");
//首页
shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SessionsSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
- 登录接口,及几个不同权限控制的测试接口
@RestController
@Slf4j
public class SysLoginController {
@GetMapping("/login")
public String login(SysUser user) {
if (StrUtil.isEmpty(user.getUserName()) || StrUtil.isEmpty(user.getPassword())) {
return "请输入用户名和密码!";
}
//用户认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
user.getUserName(),
user.getPassword()
);
try {
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
// subject.checkRole("admin");
// subject.checkPermissions("query", "add");
} catch (UnknownAccountException e) {
log.error("用户名不存在!", e);
return "用户名不存在!";
} catch (AuthenticationException e) {
log.error("账号或密码错误!", e);
return "账号或密码错误!";
} catch (AuthorizationException e) {
log.error("没有权限!", e);
return "没有权限";
}
return "login success";
}
@RequiresRoles("admin") // 角色拦截
@GetMapping("/admin")
public String admin() {
return "admin success!";
}
@RequiresPermissions("data:query")
@GetMapping("/index")
public String index() {
return "index success!";
}
@RequiresPermissions("data:add") // 权限拦截
@GetMapping("/add")
public String add() {
return "add success!";
}
}
注解验证角色和权限的话无法捕捉异常,从而无法正确的返回给前端错误信息,所以这里加了一个类用于拦截异常,具体代码如下
package com.boo.spring_boot_demo.framework.config;
import lombok.extern.slf4j.Slf4j;
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;
/** 统一异常处理
* description: MyExceptionHandler <br>
* author: boo <br>
*/
@ControllerAdvice // 它是一个Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理
@Slf4j
public class MyExceptionHandler {
@ExceptionHandler
@ResponseBody
public String ErrorHandler(AuthorizationException e) {
log.error("没有通过权限验证!", e);
return "没有通过权限验证!";
}
}
- 启动项目,登录并访问接口,验证权限;登录账户参考上
SysLoginServiceImpl.java
中信息
整合mybatis
- 引入依赖
<!-- mybatis start -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- mybatis end -->
<!-- mysql 数据库连接 start -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mysql 数据库连接 end -->
- 配置
spring:
# 数据库配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: xxxxxxxx
password: xxxxxxxx
mybatis:
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapper-locations: classpath*:mapper/system/*Mapper.xml
# 搜索指定包内的实体
typeAliasesPackage: com.boo.domain
# 配置不同包下的日志打印级别
logging:
level:
com:
boo:
# 打印sql语句
mapper: debug
- 一个简单的数据库查询例子 (查询用户列表)
创建一张表,随便添加几条数据-- 创建表sql CREATE TABLE `sys_user` ( `id` varchar(32) NOT NULL, `user_name` varchar(30) NOT NULL, `password` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统用户表'; INSERT INTO `springboot`.`sys_user` (`id`, `user_name`, `password`) VALUES ('cs001', 'zhangsan', '123456789'); INSERT INTO `springboot`.`sys_user` (`id`, `user_name`, `password`) VALUES ('cs002', 'lisi', '123456789'); INSERT INTO `springboot`.`sys_user` (`id`, `user_name`, `password`) VALUES ('cs003', 'wangwu', '123456789'); INSERT INTO `springboot`.`sys_user` (`id`, `user_name`, `password`) VALUES ('cs004', 'zhaoliu', '123456789');
3.1 SysUser.java
与上一致
3.2 SysLoginController.java
新增一个查询用户列表的接口
```java
@RestController
@Slf4j
public class SysLoginController {
@Autowired
private ISysLoginService loginService;
/* 功能描述: 查询用户列表
* @param user
* @return: java.util.List<com.boo.domain.system.SysUser>
* @Author: boo
* @Date: 2021/3/23 16:41
*/
@RequiresPermissions("data:query")
@GetMapping("/userList")
public List<SysUser> userList(SysUser user) {
return loginService.selectUserList(user);
}
}
```
3.3 `SysLoginServiceImpl.java`新增一个查询用户列表的接口
```java
@Service
public class SysLoginServiceImpl implements ISysLoginService {
@Autowired
private SysUserMapper sysUserMapper;
/** 查询用户列表 */
@Override
public List<SysUser> selectUserList(SysUser user) {
return sysUserMapper.selectUserList(user);
}
}
```
3.4 `SysUserMapper.java`
```java
package com.boo.mapper.system;
import com.boo.domain.system.SysUser;
import java.util.List;
/**
* description: SysUserMapper <br>
* date: 2021/3/23 16:32 <br>
* author: boo <br>
* version: 1.0 <br>
*/
public interface SysUserMapper {
List<SysUser> selectUserList(SysUser user);
}
```
3.5 `SysUserMapper.xml`
```xml
<?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.boo.mapper.system.SysUserMapper">
<resultMap type="com.boo.domain.system.SysUser" id="SysUserResult">
<id property="id" column="id" />
<result property="userName" column="user_name" />
<result property="password" column="password" />
</resultMap>
<select id="selectUserList" parameterType="com.boo.domain.system.SysUser" resultMap="SysUserResult">
SELECT
u.id,
u.user_name,
u.password
FROM
sys_user u
<where>
<if test="userName != null and userName != ''">
AND r.user_name = #{userName}
</if>
</where>
</select>
</mapper>
```
- 项目启动
报错:org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.boo.mapper.system.SysUserMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
原因:mapper没有注册
解决方式1: 在mapper接口上添加@Mapper
注解
解决方式2:配置全局的mapper扫描注册
在启动类上加@MapperScan(*要扫描的包*)
注解
解决方式3:写一个专门的注解配置的配置类ApplicationConfig.java
方式同2,只不过把@MapperScan
注解单独放在配置类中,保证SpringBootDemoApplication.java
(启动类)的简介, 配置类内容如下:
package com.boo.framework.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/** 功能描述: 程序注解配置
* 主要放一些注解配置
* @Author: boo
* @Date: 2021/3/24 8:53
*/
@Configuration
@MapperScan("com.boo.mapper") // 指定要扫描的Mapper类的包的路径
public class ApplicationConfig
{
}