visionzheng 2020-04-20
https://www.jianshu.com/p/d0b354cc5147
Springboot使用的页面模板是thyme leaf,它比jsp多一些特定的标签,可以动态或者静态显示文本内容
1、spring boot中导入thymeleaf页面模板
(1) 在pom.xml文件引入thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>(2)在application.properties(application.yml)文件中配置thymeleaf
thymeleaf:
cache: false # 关闭页面缓存
encoding: UTF-8 # 模板编码
prefix: classpath:/templates/ # 页面映射路径
suffix: .html # 试图后的后缀
mode: HTML5 # 模板模式(3)测试导入的thymeleaf页面
/**
* 测试thymeleaf
*/
@RequestMapping("/testThymeleaf")
public String testThymeleaf(Model model){
//把数据存入model
model.addAttribute("name","qmh");
//model.addAttribute默认返回的.html文件,所以这里return的是test.html
return "/test";
}取测试页面的name名字,建立test.html页面(注意:spring boot建立html的地方为src/main/java/resources/temelates中)
<!DOCTYPE html>
<!--
~ /*
~ * Copyright (c) 2019. tdc.shangri-la.com. All Rights Reserved.
~ */
-->
<br lang="en" xmlns:th="http://www.w3.org/1999/xhtml" >
<head>
<meta charset="UTF-8">
<title>测试thymeleaf的使用</title>
</head>
<body>
<hr>
<!--获取name-->
<h2 th:text="${name}"></h2>
</hr>
</body>
</html>2.Springboot与Shiro整合实现用户认证
2.1shiro核心ApI
* subject(包括用户登录,注销、授权的方法,需要关联securityManager)* securityManager:安全管理器(需要连接realm)* realm:shiro连接数据的桥梁2.2.Springboot与Shiro整合(1)导入shiro与spring的依赖
<!--导入shiro与spring整合的依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>(2)自定义realm类,realm类用来编写认证或授权的方式方法或者逻辑,完成shiro与其他应用的数据连接
realm类需要继承AuthorizingRealm ,实现doGetAuthorizationInfo(授权)、doGetAuthenticationInfo(认证)的方法
package com.toxic.anepoch.boot2.controller.web.Shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* Title:
* Description:自定义realm类,实现shiro框架中realm部分的逻辑操作,
* (1)与securitymanager部分的关联
* (2)完成shiro与数据库的连接
*
* @author py
* @date 2020/4/16 20:07.
*
*/
public class UserRealm extends AuthorizingRealm {
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权认证");
return null;
}
//执行认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行逻辑认证");
return null;
}
}(2)编写shiro的配置类ShiroConfigTest
创建三个对象ShiroFilterFactoryBean、DefaultSecurityManager、realm
package com.toxic.anepoch.boot2.controller.web.Shiro;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
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.Map;
/**
* Title:
* @author py
* @date 2020/4/16 19:42.
*/
//@Configuration 注解,表示声明该类为Spring 的配置类
@Configuration
public class ShiroConfigTest {
// 创建三个对象
/**
* 创建ShiroFilterFactoryBean(关联manager)
*/
//@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager ){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
return shiroFilterFactoryBean;
}
/**
*创建DefaultSecurityManager(关联realm对象)
*/
//@Bean//@qualifier,qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,该注解是用来消除依赖注入冲突的
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
/**
*创建realm(自定义一个realm实体类,通过实体类进行调用)
*/
//创建一个方法,方法返回对象即我们要创建的对象 UserRealm ,返回该对象的实例。在方法上打上注解@Bean即表示声明该方法返回的实例是受 Spring 管理的 Bean,便于其他方法使用
@Bean
public UserRealm getUserBealm(){
UserRealm userRealm = new UserRealm();
return userRealm;
}
}2.3Shiro常用的内置过滤器实现url页面的拦截
* 常用的一些过滤器等级:* anon:无需认证(登录)就可以访问* authc:必须认证才可以访问* user:如果使用rememberMe的功能,可以直接访问* perms;该资源必须授予资源权限才可以访问* role;该资源必须授予角色权限才可以访问
package com.toxic.anepoch.boot2.controller.web.Shiro;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
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.Map;
//@Configuration 注解,表示声明该类为Spring 的配置类
@Configuration
public class ShiroConfigTest {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager ){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器等级
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加shiro的内置过滤器,实现url的相关的拦截
/**
* 常用的一些过滤器等级:
* anon:无需认证(登录)就可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能,可以直接访问
* perms;该资源必须授予资源权限才可以访问
* role;该资源必须得到角色权限才可以访问
*/
//创建一个map集合,用来参访需要拦截的url路径(controller中 @RequestMapping("/"))即及改路径的过滤等级,这里创建LinkedHashMap集合,是为了存入的数据具有顺序
Map<String,String> filterMap = new LinkedHashMap<>();
//若拦截生效后默认跳转到login.jsp
filterMap.put("/add","authc");
filterMap.put("/update","authc");
/*
要想对user下所有的url都过滤,那么可以用* (*通配符)
filterMap.put("/user/*","authc");
*/
/*
*想对某一个url做放行处理可以用以下方法
* filterMap.put("/testThymeleaf","anon");
* */
//修改拦截的时侯跳转的页面(这里用的authc等级,就可以理解为要先完成登录),登录页面都从controller层进行关联,toLogin是controller的方法,用来访问login登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
*创建DefaultSecurityManager(关联realm对象)
*/
@Bean(value = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
/**
*创建realm(自定义一个realm实体类,通过实体类进行调用)
*/
//在创建一个方法,方法返回对象即我们要创建的对象 UserRealm , 返回该对象的实例。在方法上打上注解@Bean即表示声明该方法返回的实例是受 Spring 管理的 Bean,便于其他方法使用
@Bean(value = "userRealm")
public UserRealm getUserBealm(){
UserRealm userRealm = new UserRealm();
return userRealm;
}
}2.4Shiro用户登录操作创建一个登陆页面:
<!DOCTYPE html>
<!--
~ /*
~ * Copyright (c) 2019. tdc.shangri-la.com. All Rights Reserved.
~ */
-->
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>用户的登录页面</title>
</head>
<body>
<h3>登录</h3>
<!--获取controller中用户名或是密码的错误信息-->
<p color="red" th:text="${msg}"></p>
<form method="post" action="login">
用户名:<input type="text" name="name"> </br>
密码:<input type="password" name="password"></br>
<input type="submit" value="登录">
</form>
</body>
</html>controller的登录逻辑:
//登陆的逻辑处理,接受表单中的name,password两个参数
@RequestMapping("/login")
public String login(String name,String password,Model model){
//使用shiro编写认证操作
//1.获取subject对象
Subject subject = SecurityUtils.getSubject();
//2.封装用户的数据,将表单中的数据传递给token对象
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//3.执行登录方法,这里使用try...catch来判断是否登录成功,若没有异常则登陆成功,有异常在进行判断并抛出异常
try {
subject.login(token);
System.out.println("登陆成功");
//登陆成功跳转到主页test.html (redirect:重定向)
return "redirect:/hello/testThymeleaf";
} catch (UnknownAccountException e) {
//用户名不存在,信息返回到html页面
model.addAttribute("msg","用户名不存在");
//返回到登陆页面
return "login";
}
catch (IncorrectCredentialsException e) {
//密码错误,信息返回到html页面
model.addAttribute("msg","密码错误");
//返回到登陆页面
return "login";
}package com.toxic.anepoch.boot2.controller.web.Shiro;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* Title:
* Description:自定义realm类,实现shiro框架中realm部分的逻辑操作,
* (1)与securitymanager部分的关联
* (2)完成shiro与数据库的连接
*
* @author py
* @date 2020/4/16 20:07.
*
*/
public class UserRealm extends AuthorizingRealm {
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权认证");
return null;
}
//执行认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken ) throws AuthenticationException {
System.out.println("执行逻辑认证");
//假设数据库的用户名和密码分别是:qiu,123456
String name="qiu";
String password="123456";
//编写shiro的判断逻辑,判断用户名和密码是否正确
//authenticationToken类型强制转换成controller层中的UsernamePasswordToken类型,用来存放name和password
// (controller中token的作用是:封装用户的数据,将表单中的数据传递给token对象,具体操作:UsernamePasswordToken token = new UsernamePasswordToken(name,password);)
UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken;
if(!Token.getUsername().equals(name)){
System.out.println("用户名不存在");
return null;//shiro的底层会抛出UnknownAccountException异常
}else{
//判断密码,直接创建AuthorizationInfo对象的子对象,传入password参数就可以判断密码是否一致
return new SimpleAuthenticationInfo("",password,"");
}
}
}2.5整合myBatis实现用户的登录2.5.1导入myBatis的相关的依赖
<!-- mybatis-plus begin mybtis启动器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot-starter</artifactId>
<version>${mybatisplus-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<!-- mybatis-plus end -->
<!--sharding-jdbc start -->
<dependency>
<groupId>io.shardingjdbc</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${sharding-sphere.version}</version>
</dependency>
<!-- sharding-jdbc end -->
<!--mysql start-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
<scope>runtime</scope>
</dependency>
<!--mysql end-->
<!-- druid 数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${com.alibaba.druid.version}</version>
</dependency>
</dependencies>2.5.2创建application.properties或application.yml文件,用来设置一些参数
//数据库的配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#druid相关配置
driver-class-name: com.mysql.cj.jdbc.Driver
#基本属性
url: jdbc:mysql:
username:
password:
//mybatis的配置
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: com.toxic.anepoch.boot2.service.bean2.5.3在com.toxic.anepoch.boot2.service.bean包下创建user实体类
package com.toxic.anepoch.boot2.service.bean;
/**
* Title:
* Description:
*
* @author py
* @date 2020/4/19 22:15.
*/
public class User {
private int id;
private String name;
private String password;
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name=‘" + name + ‘\‘‘ +
", password=‘" + password + ‘\‘‘ +
‘}‘;
}2.5.4创建mapper接口实现与数据库的关联并创建mapper.xml文件
package com.toxic.anepoch.boot2.service.dao;
import com.toxic.anepoch.boot2.service.bean.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
/**
* Title:查询用户
* Description:
*
* @author py
* @date 2020/4/19 22:22.
*/
@Repository
//@Repository用在持久层的接口上,这个注解是将接口的一个实现类交给spring管理。
public interface UserMapper {
User findByName(@Param(value = "name") String name);
}<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ /*
~ * Copyright (c) 2019. tdc.shangri-la.com. All Rights Reserved.
~ */
-->
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 不使用namespace的话sql搜索定位会比较方便 -->
<mapper namespace="com.toxic.anepoch.boot2.service.dao.UserMapper">
<resultMap id="UserInfoResultMap" type="com.toxic.anepoch.boot2.service.bean.User">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="password" column="password"/>
</resultMap>
<!-- 用于select查询公用抽取的列 -->
<sql id="UserInfoColumns">
<![CDATA[
id,name,password
]]>
</sql>
<select id="findByName" resultMap="UserInfoResultMap">
SELECT <include refid="UserInfoColumns" />
<![CDATA[
FROM user
WHERE
name = #{name}
]]>
</select>
</mapper>2.5.5编写service接口和实现类,用于调用mepper接口
package com.toxic.anepoch.boot2.service.service;
import com.toxic.anepoch.boot2.service.bean.User;
/**
* Title:
* Description:
*
* @author py
* @date 2020/4/19 22:30.
*/
public interface UserService {
/**
*通过名字进行查询
*/
User findByName(String name);
}package com.toxic.anepoch.boot2.service.service.impl;
import com.toxic.anepoch.boot2.service.bean.User;
import com.toxic.anepoch.boot2.service.dao.UserMapper;
import com.toxic.anepoch.boot2.service.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* Title:
* Description:
*
* @author py
* @date 2020/4/19 22:34.
*/
@Service
public class UserServiceImpl implements UserService{
@Resource
// @Resource注入mapper接口
private UserMapper userMapper;
@Override
public User findByName(String name) {
return userMapper.findByName( name);
}
}2.5.5编写Biz接口和实现类,用于调用service接口和业务的创建
package com.toxic.anepoch.boot2.biz.business;
import com.toxic.anepoch.boot2.service.bean.User;
/**
* Title:
* Description:
*
* @author py
* @date 2020/4/19 22:58.
*/
public interface UserBiz {
User findByName(String name);
}/**
* Title:
* Description:
*
* @author py
* @date 2020/4/19 22:59.
*/
@Component
public class UserBizImpl implements UserBiz{
@Resource
private UserService userService;
@Override
public User findByName(String name) {
return userService.findByName(name);
}
}对UserRealm类进行修改
public class UserRealm extends AuthorizingRealm {
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权认证");
return null;
}
@Autowired
//@Autowired注入UserBiz接口
private UserBiz userBiz;
//执行认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken ) throws AuthenticationException {
System.out.println("执行逻辑认证");
//假设数据库的用户名和密码分别是:qiu,123456
// String name="qiu";
// String password="123456";
//编写shiro的判断逻辑,判断用户名和密码是否正确
//authenticationToken类型强制转换成controller层中的UsernamePasswordToken类型,用来存放name和password
// (controller中token的作用是:封装用户的数据,将表单中的数据传递给token对象,具体操作:UsernamePasswordToken token = new UsernamePasswordToken(name,password);)
UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken;
User user = userBiz.findByName(Token.getUsername());
if(user==null){
System.out.println("用户名不存在");
return null;//shiro的底层会抛出UnknownAccountException异常
}else{
//判断密码,直接创建AuthorizationInfo对象的子对象,传入password参数就可以判断密码是否一致
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
}
}3.spring boot与shiro整合实现用户授权
3.1使用shiro的内置过滤器拦截资源
常用的授权过滤器:perms,role,授权过滤器与认证过滤器不同的是:授权过滤器需要加授权字符串,这里给add接口添加授权
//添加授权过滤器,授权字符串,授权字符串可以任意的指定
filterMap.put("/add","perms:[user:add]");未授权过滤器生效后,会自动跳转到默认的未授权页面,当然也可以自定义一个未授权的页面
需要在controller层建立一个/noAuth路径去访问noAuth页面
//设置未授权页面,授权过滤器生效,跳转到noAuth页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");3.2编写资源授权逻辑
public class UserRealm extends AuthorizingRealm {
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权认证");
//给资源授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加getShiroFilterFactoryBean方法中设置的授权字符串
info.addStringPermission("user:add");
return info;
}
}3.3关联数据库实现动态授权