Spring boot学习笔记——狂神说

SpringBoot学习笔记

1.1 简介

Spring Boot 是 Pivotal 团队在 Spring 的基础上提供的一套全新的开源框架,其目的是为了简化 Spring 应用的搭建和开发过程。Spring Boot 去除了大量的 XML 配置文件,简化了复杂的依赖管理。

springboot配置文件名称是固定的:

  • application.properties 语法结构:key=value
  • application.yaml. 语法结构:key:空格 value

1.2 导入依赖

<!--web端:自动配置tomcat-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--开启热启动-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-devtools</artifactId>
</dependency>

1.3 Spring boot核心配置文件(application.properties)

#更改项目的端口号
server.port=8090

改变banner

在这里插入图片描述

在这里插入图片描述

1.4 Spring boot核心配置文件(application.yaml)

#key:空格value
server:
  port: 8091
#对象
student:
  name: songyaxuan
  age: 18
#行内写法
students: {name: liuyaowen,age: 16}
#数组
pets:
  -cat
  -dog
pet: [cat,dog]

yaml可以给实体类赋值

原来用@value给实体类赋值:

@Component
public class Dog {
    @Value("鼠标")
    private String name;
    @Value("1")
    private Integer age;
    }
@SpringBootTest
class SpbootApplicationTests {
    @Autowired
    private Dog dog;
    @Test
    void contextLoads() {
        System.out.println(dog);
    }
}

在这里插入图片描述

现在通过yaml赋值:

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}
server:
  port: 8091
person:
  name: songysxuan
  age: 18
  happy: true
  birth: 2004/03/04
  maps: {k1: v2,k2: v2}
  lists:
    -music
    -liu
  dog:
    name: shubiao
    age: 1

@SpringBootTest
class SpbootApplicationTests {
    @Autowired
    private Person person;
    @Test
    void contextLoads() {
        System.out.println(person);
    }
}

在这里插入图片描述

绑定指定配置文件并给实体类属性赋值:

tnt.properties:

name=shidaishaoniantuan
age=2

Person.java:

@Component
@PropertySource(value = "classpath:tnt.properties")
public class Person {
    //yaml支持el表达式
    @Value("${name}")
    private String name;
    @Value("${age}")
    private Integer age;
    }

测试类:

@SpringBootTest
class SpbootApplicationTests {
    @Autowired
    //private Dog dog;
    private Person person;
    @Test
    void contextLoads() {
        System.out.println(person.getName());
        System.out.println(person.getAge());
    }
}

在这里插入图片描述

1.5 Springboot Web开发

需要解决的问题:

  • 导入静态资源
  • 首页定制
  • jap–模版引擎thymeleaf
  • 装配扩展springMVC
  • 增删改查
  • 拦截器

1.5.1 静态资源

导入依赖:

<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>jquery</artifactId>
  <version>3.4.1</version>
</dependency>

访问静态资源:

http://localhost:8080/webjars/jquery/3.4.1/jquery.js

在这里插入图片描述

或者创建目录(优先级:resource>statis>public
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.5.2 首页定制

在templates目录下的所有页面,只能通过controller来跳转!

在这里插入图片描述

@RestController
public class IndexController {
    @RequestMapping("/index")
    public String index(){
        return "index";
    }
}

在这里插入图片描述

1.5.3 模版引擎thymeleaf

导入依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

导入依赖以后只需要将html放入templates中,就可以通过controller访问来。

html文件中引入:

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 <div th:text="${msg}"></div>
</body>
</html>

1.5.4 装配扩展MVC

先创建一个包config,在里面写一个类

在这里插入图片描述

//扩展mvc,implements WebMvcConfigurer并且不可以加注解@EnableWebMvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    //视图跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
          //请求/candy时页面跳转到hello页面
        registry.addViewController("/candy").setViewName("hello");
    }
}

在这里插入图片描述

1.6 项目实战

1.6.1 准备工作

引入Lombok:

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>

@Data:使用这个注解可以省去实体类中大量的get()、 set()、 toString()等方法。

@AllArgsConstructor:有参构造

@NoArgsConstructor:无参构造

注意:以上注解都是在Lombok中的!!!

package com.candy.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
//有参构造
@AllArgsConstructor
//无参构造
@NoArgsConstructor
public class Department {
    private Integer id;
    private String  departmentName;
}

在这里插入图片描述

导入模板:

在这里插入图片描述

编写实体类和dao层:

在这里插入图片描述

Employee.java:

package com.candy.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;//0女1男
    private Department department;
    private Date birth;
}

DepartmentDao.java:

package com.candy.dao;
import com.candy.pojo.Department;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@Repository
public class DepartmentDao {
    private static Map<Integer, Department> departments = null;
    static {
    departments = new HashMap<Integer, Department>();//创建一个部门表

    departments.put(101,new Department(101,"教学部"));
    departments.put(102,new Department(102,"市场部"));
    departments.put(103,new Department(103,"教研部"));
    departments.put(104,new Department(104,"运营部"));
    departments.put(105,new Department(105,"财务部"));

    }
    //获得所有部门信息
    public Collection<Department> getDepartments(){
        return  departments.values();
    }
    //通过id得到部门
    public Department getDepartmenrById(Integer id){
        return departments.get(id);
    }
}

EmployeeDao.java:

package com.candy.dao;
import com.candy.pojo.Department;
import com.candy.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Repository
public class EmployeeDao {
    private static Map<Integer,Employee> employees = null;
    @Autowired
    private DepartmentDao departmentDao;
    static {
        employees = new HashMap<Integer,Employee>();//创建一个员工表
        employees.put(1001,new Employee(1001,"马嘉祺","11@qq.com",1,new Department(101,"教学部"),new Date()));
        employees.put(1002,new Employee(1002,"丁程鑫","12@qq.com",1,new Department(101,"教学部"),new Date()));
        employees.put(1003,new Employee(1003,"宋亚轩","13@qq.com",1,new Department(102,"市场部"),new Date()));
        employees.put(1004,new Employee(1004,"刘耀文","14@qq.com",1,new Department(102,"市场部"),new Date()));
        employees.put(1005,new Employee(1005,"张真源","15@qq.com",1,new Department(103,"教研部"),new Date()));
        employees.put(1006,new Employee(1006,"严浩翔","16@qq.com",1,new Department(104,"运营部"),new Date()));
        employees.put(1007,new Employee(1007,"贺峻霖","17@qq.com",1,new Department(104,"运营部"),new Date()));

    }
    //主键自增
    private static Integer initId = 1008;
    //增加一个员工
    public void add(Employee employee){
        if (employee.getId()==null){
            employee.setId(initId++);
        }      employee.setDepartment(departmentDao.getDepartmenrById(employee.getDepartment().getId()));
        employees.put(employee.getId(),employee);
    }
    //查询所有员工
    public Collection<Employee> getAll(){
        return employees.values();
    }
    //根据ID查询员工
    public Employee getEmploeeById(Integer id){
        return employees.get(id);
    }
    //删除员工
    public void del(Integer id){
        employees.remove(id);
    }
}

1.6.2 首页实现

先配置路径,可以访问index(在config的MyMvcConfig.java):

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("index.html").setViewName("index");
    }
}

导入thymeleaf在模板中,并按照规则修改标签中的属性

#关闭thymeLeaf缓存
spring.thymeleaf.cache=false
<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<meta name="description" content="">
		<meta name="author" content="">
		<title>Signin Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
		<!-- Custom styles for this template -->
		<link th:href="@{/css/signin.css}" rel="stylesheet">
	</head>

在这里插入图片描述

1.6.3 页面国际化

实现页面中文和英文语言切换!!!

先在resource目录下创建一个包:
在这里插入图片描述
在这里插入图片描述

然后在application.properties中写好配置文件的路径:

#配置文件的位置
spring.messages.basename=i18n.login

在前端页面中使用语法取值:

	<form class="form-signin" action="dashboard.html">
			<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please login</h1>
			<label class="sr-only" >Username</label>
			<input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
			<label class="sr-only">Password</label>
			<input type="password" class="form-control" th:placeholder="#{login.password}" required="">
			<div class="checkbox mb-3">
				<label >
          <input type="checkbox" value="remember-me" th:text="#{login.remember}">
        </label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Login</button>
			<p class="mt-5 mb-3 text-muted">© 2022-2023</p>
			<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
			<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
		</form>

为了实现中文和English的按钮,我们自己需要自定义国际化,自己写一个类:

在这里插入图片描述

package com.candy.comfig;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;


public class MyLocalResolver implements LocaleResolver {
     //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
      String  language = request.getParameter("l");
      Locale locale = Locale.getDefault();//如果没有就使用默认的
        //如果请求链接携带了国际化的参数
      if (!StringUtils.isEmpty(language)){
        String[]  split =  language.split("_");
        locale =  new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

然后在mymvcconfig中注册:

   //自定义国际化
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocalResolver();
    }

在这里插入图片描述
在这里插入图片描述

注意:需要先在idea设置的file encoding中将编码语言全部设置为utf-8,否则可能会乱码。

1.6.4 登陆功能实现

在前端页面中设置按钮请求:

		<form class="form-signin" th:action="@{/user/login}">

然后写一个controller类管理登陆业务:

package com.candy.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class LoginController {
    @RequestMapping("/user/login")
    public String login(
            @RequestParam("username") String username,
            @RequestParam("password") String password,
            Model model
             ){
           if(!StringUtils.isEmpty(username)&&"123".equals(password)){
                //登陆成功之后重定向页面
               return "redirect:/main.html";
           }else {
               model.addAttribute("msg","用户名或者密码错误");
               return "index";
           }
    }
}

重定向页面需要在MyMvcConfig.java中配置:

  registry.addViewController("main.html").setViewName("dashboard");

在这里插入图片描述

1.6.5 登陆拦截器

需要自己写一个拦截类:

在这里插入图片描述

package com.candy.comfig;

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //登陆成功之后应该有用户的session
      Object   loginUser = request.getSession().getAttribute("loginUser");
       if (loginUser==null){//没有登陆
           request.setAttribute("msg","没有权限,请先登陆");
           request.getRequestDispatcher("/index.html").forward(request,response);
           return false;
       }else {
           return true;
       }
    }
}

在loginController.java中添加session(并将用户名放入session中保存):

在这里插入图片描述

最后在MyMvcConfig.java中注册配置类:

 @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/css/**","/img/**","/img/**");

    }

解释:在所有路径下应用,除了/index.html、/、/user/login、所有静态资源

在前端页面中取出用户名:

[[${session.loginUser}]]

直接访问main.html会被拦截器阻拦:

在这里插入图片描述

正常登陆后,会显示用户名:

在这里插入图片描述

1.6.6 展示员工页面

抽取公布组件:

新建文件commons,创建commons.html存放公共的导航栏:

在这里插入图片描述

首页在commons.html中引入

<html lang="en" xmlns:th="http://www.thymeleaf.org">

将原来dashboard和list中公共的头部导航和侧边导航栏:

<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
    <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a>
    <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
    <ul class="navbar-nav px-3">
        <li class="nav-item text-nowrap">
            <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">注销</a>
        </li>
    </ul>
</nav>
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
    <div class="sidebar-sticky">
        <ul class="nav flex-column">
            <li class="nav-item">
                <a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/main.html}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
                        <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                        <polyline points="9 22 9 12 15 12 15 22"></polyline>
                    </svg>
                    首页 <span class="sr-only">(current)</span>
                </a>
            </li>
            <li class="nav-item">
                <a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users">
                        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                        <circle cx="9" cy="7" r="4"></circle>
                        <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                        <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                    </svg>
                    员工管理
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2">
                        <line x1="18" y1="20" x2="18" y2="10"></line>
                        <line x1="12" y1="20" x2="12" y2="4"></line>
                        <line x1="6" y1="20" x2="6" y2="14"></line>
                    </svg>
                    部门管理
                </a>
            </li>
        </ul>
    </div>
</nav>

dashboard和list中引入:

在这里插入图片描述

在这里插入图片描述

展示员工信息(循环取值):

<table class="table table-striped table-sm">
							<thead>
								<tr>
									<th>id</th>
									<th>lastName</th>
									<th>email</th>
									<th>gender</th>
									<th>department</th>
									<th>birth</th>
									<th>操作</th>
								</tr>
							</thead>
							<tbody>
								<tr th:each="emp:${emps}">
                   <td th:text="${emp.getId()}"></td>
									 <td th:text="${emp.getLastName()}"></td>
									 <td th:text="${emp.getEmail()}"></td>
									 <td th:text="${emp.getGender()==0?'':''}"></td>
									 <td th:text="${emp.department.getDepartmentName()}"></td>
									 <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}">     </td>
									 <td>
										 <button class="btn btn-sm btn-primary">edit</button>
										 <button class="btn btn-sm btn-danger">del</button>
									 </td>
								</tr>
							</tbody>
						</table>

在这里插入图片描述

1.6.7 添加、修改、删除员工以及404处理和注销

  • 添加员工

先把add.html写出来(用list.html改写)

<div class="container-fluid">
			<div class="row">
		       <div th:insert="~{commons/commons::sidebar(active='list.html')}"></div>

				<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
					<form th:action="@{/emp}" method="post">
						<div class="form-group">
							<label>lastName</label>
							<input type="text"  name="lastName" class="form-control"  placeholder="weishenme">
						</div>
						<div class="form-group">
							<label>Email</label>
							<input type="email" name="email" class="form-control"  placeholder="11@qq.com">
						</div>
						<div class="form-group">
							<label>Gender</label>
							<div class="form-check form-check-inline" >
							   <input class="form-check-input" type="radio" name="gender" value="1">
								<label class="form-check-label"></label>
							</div>
							<div class="form-check form-check-inline" >
								<input class="form-check-input" type="radio" name="gender" value="0">
								<label class="form-check-label"></label>
							</div>
						</div>
						<div class="form-group">
							<label>department</label>
							<select class="form-control" name="department.id" >
								<option th:each="dept:${dep}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
							</select>
						</div>

						<div class="form-group">
							<label >Birth</label>
							<input type="text" class="form-control" name="birth" placeholder="tnt">
						</div>
						<button type="submit" class="btn btn-primary">add</button>
					</form>
				</main>
			</div>
		</div>

然后去写controller中添加员工的方法:

    @GetMapping("/emp")
    public String toAddpage(Model model){
        //查出部门所有的信息
    Collection<Department>  departments =  departmentDao.getDepartments();
    model.addAttribute("dep",departments);
        return "emp/add";
    }
    @PostMapping("/emp")
    public String addEmp(Employee employee){
        //添加的操作 formward
        System.out.println(employee);
        employeeDao.add(employee);//保存员工信息
        return "redirect:/emp";
    }

在这里插入图片描述

注意:

#时间日期格式化
spring.mvc.format.date=yyyy-MM-dd
  • 修改员工

添加一个update.html页面(由add.html页面copy)

<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
					<form th:action="@{/updateEmp}" method="post">
						<input type="hidden" name="id" th:value="${emp.getId()}">
						<div class="form-group">
							<label>lastName</label>
							<input type="text"  th:value="${emp.getLastName()}" name="lastName" class="form-control"  placeholder="weishenme">
						</div>
						<div class="form-group">
							<label>Email</label>
							<input type="email" th:value="${emp.getEmail()}" name="email" class="form-control"  placeholder="11@qq.com">
						</div>
						<div class="form-group">
							<label>Gender</label>
							<div class="form-check form-check-inline" >
							   <input th:checked="${emp.getGender()==1}" class="form-check-input" type="radio" name="gender" value="1">
								<label class="form-check-label"></label>
							</div>
							<div class="form-check form-check-inline" >
								<input th:checked="${emp.getGender()==0}" class="form-check-input" type="radio" name="gender" value="0">
								<label class="form-check-label"></label>
							</div>
						</div>
						<div class="form-group">
							<label>department</label>
							<select class="form-control" name="department.id" >
								<option  th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
							</select>
						</div>

						<div class="form-group">
							<label >Birth</label>
							<input  th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}" type="text" class="form-control" name="birth" placeholder="tnt">
						</div>
						<button type="submit" class="btn btn-primary">done</button>
					</form>
				</main>

在controller层加入修改的方法:

  @GetMapping("emp/{id}")
      public String toUpdateEmp(@PathVariable("id")Integer id,Model model){
          Employee employee = employeeDao.getEmploeeById(id);
          model.addAttribute("emp",employee);
          Collection<Department>  departments =  departmentDao.getDepartments();
          model.addAttribute("departments",departments);
           return "emp/update";
      }
      @PostMapping("updateEmp")
      public String updateEmp(Employee employee){
        employeeDao.add(employee);
        return "redirect:/emps";
      }

在这里插入图片描述

在这里插入图片描述

  • 删除员工
 <a class="btn btn-sm btn-danger" th:href="@{delemp/{empId}(empId=${emp.id})}">del</a>
  @GetMapping("delemp/{id}")
    public String delEmp(@PathVariable("id")Integer id){
        employeeDao.del(id);
        return "redirect:/emps";
    }
  • 404处理

在template文件中创建一个error文件里面放入一些处理错误的页面,idea会自动配置路径:

在这里插入图片描述

  • 注销
<li class="nav-item text-nowrap">
  <a class="nav-link" th:href="@{/user/logout}">注销</a>
</li>
  @RequestMapping("/user/logout")
    public String logout(HttpSession session){
         session.invalidate();
         return "redirect:/index.html";
    }

1.7 Spring Data

1.7.1 整合JDBC使用

添加依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

编写application.yaml:

  spring:
    datasource:
      username: root
      password: root12345
      url: jdbc:mysql://localhost:3306/mybts?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver

测试:

@SpringBootTest
class SpdataApplicationTests {
    @Autowired
    DataSource dataSource;
    @Test
    void contextLoads() {
        //查看默认数据源
        System.out.println(dataSource.getClass());
        //获得数据库连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        //关闭连接
        connection.close();
    }
}

在这里插入图片描述

查询数据库内容:

在这里插入图片描述

@RestController
public class JDBCController {
       @Autowired
       JdbcTemplate jdbcTemplate;
       //查询数据库的所有信息
       @GetMapping("/userList")
       public List<Map<String,Object>> userList(){
            String sql = "select * from mybts.user";
            List<Map<String,Object>> maps = jdbcTemplate.queryForList(sql);
            return maps;
       }
}

在这里插入图片描述

1.7.2 整合Druid数据源

导入druid依赖:

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.7</version>
</dependency>

在配置文件中选择数据库的数据源类型:

spring:
  datasource:
    username: root
    password: root12345
    url: jdbc:mysql://localhost:3306/mybts?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

在这里插入图片描述

druid常用配置:

druid:
  #     配置初始化大小、最小、最大线程数
  initialSize: 5
  minIdle: 5
  #     CPU核数+1,也可以大些但不要超过20,数据库加锁时连接过多性能下降
  maxActive: 20
  #     最大等待时间,内网:800,外网:1200(三次握手1s)
  maxWait: 60000
  timeBetweenEvictionRunsMillis: 60000
  #     配置一个连接在池中最大空间时间,单位是毫秒
  minEvictableIdleTimeMillis: 300000
  validationQuery: SELECT 1
  testWhileIdle: true
  #     设置从连接池获取连接时是否检查连接有效性,true检查,false不检查
  testOnBorrow: false
  #     设置从连接池归还连接时是否检查连接有效性,true检查,false不检查
  testOnReturn: false
  #     可以支持PSCache(提升写入、查询效率)
  poolPreparedStatements: true
  #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
  filters: stat,wall,log4j
  #     保持长连接
  keepAlive: true
  maxPoolPreparedStatementPerConnectionSize: 20
  useGlobalDataSourceStat: true
  connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

写一个druidconfig:

在这里插入图片描述

@Configuration
public class DruidConfig {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource(){
     return new DruidDataSource();
    }
    //后台监控
    @Bean
    public ServletRegistrationBean statViewServlet(){
     ServletRegistrationBean<StatViewServlet>  bean =  new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
      //后台账号密码配置
        HashMap<String,String> initParameters = new HashMap<>();
        //增加配置
        initParameters.put("loginUsername","admin");//key是固定的
        initParameters.put("loginPassword","123456");
        //允许谁访问  空:所有人都可以
        initParameters.put("allow","");
        //禁止谁访问   谁,域名
        //initParameters.put("candy","192.168.11.123");
        bean.setInitParameters(initParameters);//设置初始化参数
        return bean;
    }
}

在这里插入图片描述
在这里插入图片描述

web的过滤器:

//WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计 @Bean
public FilterRegistrationBean webStatFilter() {
  FilterRegistrationBean bean = new FilterRegistrationBean();
  bean.setFilter(new WebStatFilter());
  //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
  Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*"); bean.setInitParameters(initParams);
  //"/*" 表示过滤所有请求 
  bean.setUrlPatterns(Arrays.asList("/*")); 
  return bean;
}

1.7.3 整合mybatis

导入依赖:

  <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.1</version>
  </dependency>

连接数据库,创建实体类(引入Lombok):

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
     private int id;
     private String name;
     private String password;
}

写mapper.java:

@Mapper
@Repository
public interface UserMapper {
    List<User> queryUserList();
    User queryById(int id);
    int addUser(User user);
    int updateUser(User user);
    int delUser(User user);
}

创建UserMapper.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.candy.mapper.UserMapper">
    <select id="queryUserList" resultType="com.candy.pojo.User">
        select * from user
    </select>
    <select id="queryById" parameterType="int" resultType="com.candy.pojo.User" >
        select * from user where id =#{id}
    </select>
    <insert id="addUser" parameterType="com.candy.pojo.User">
        insert into user(id,name,password) values (#{id},#{name},#{password})
    </insert>
    <update id="updateUser" parameterType="com.candy.pojo.User">
        update user set name = #{name},password = #{password}
    </update>
    <delete id="delUser" parameterType="int">
        delete from user where id =#{id}
    </delete>
</mapper>

在application.yaml中写别名:

mybatis:
  type-aliases-package: com.candy.pojo
  mapper-locations:
    - classpath:mybatis/mapper/*.xml

编写controller测试:

@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;
    @GetMapping("/queryUserList")
    public List<User> queryUserList(){
        List<User> userList = userMapper.queryUserList();
        return userList;
    }
}

在这里插入图片描述

1.8 Spring Security

Spring Security is a powerful and highly customizable authentication and access-control framework.

记住以下类:

  • WebSecurityConfigurerAdapter 自定义security策略
  • AuthenticationManagerBuilder 自定义认证策略
  • @EnableWebSecurity: 开启WebSecurity模式 @Enablexxxx开启某个功能
1.8.1 Spring Security环境搭建

导入依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

导入静态资源:

https://gitee.com/plushuang/plushuang-springsecurity-project/tree/master/src/main

放入项目中:

在这里插入图片描述

关闭模版引擎缓存:

spring.thymeleaf.cache=false

编写controller:

package com.candy.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RouterController {
    @RequestMapping({"/","index"})
     public String index(){
         return "index";
     }
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "/views/login";
    }
      @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id")int id){
        return "/views/level1/"+id;
    }
    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id")int id){
        return "/views/level2/"+id;
    }
    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id")int id){
        return "/views/level3/"+id;
    }
}

进入index:

在这里插入图片描述
在这里插入图片描述

1.8.2 用户认证与授权

创建config:

package com.candy.config;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //链式编程
    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception{
        //首页所有人可以访问,但是功能页只有有权限的人才能访问
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");
        //没有权限会跳转到登陆页面
        http.formLogin().loginPage("/toLogin");
        //关闭csrf 防护
        http.csrf().disable();
    }
}

在次点击level页面后会跳转到登陆页面:

在这里插入图片描述

添加认证(重写configure(AuthenticationManagerBuilder auth) 方法):

//认证
//密码编码 passwordEncoder
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
    .withUser("123").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2")
    .and()
    .withUser("dingchengxin").password(new BCryptPasswordEncoder().encode("123")).roles("vip3")
    .and()
    .withUser("tnt").password(new BCryptPasswordEncoder().encode("123")).roles("vip1");
}

测试,登陆tnt账户:

在这里插入图片描述

在这里插入图片描述

只能访问level1的内容,没有权限访问level2和level3的内容!

连接数据库的改写方法

import javax.sql.DataSource;

@Autowired
private DataSource dataSource;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.jdbcAuthentication()
    .dataSource(dataSource)
    .usersByUsernameQuery("select username,password,enabled from users WHERE username=?")
    .authoritiesByUsernameQuery("select username,authority from authorities where username=?")
    .passwordEncoder(new BCryptPasswordEncoder());
}
1.8.3 注销及权限控制
  • 注销
//注销
http.logout();
<div sec:authorize="isAuthenticated()">
  <a class="item" th:href="@{/logout}">
    <i class="address card icon"></i> 注销
  </a>
</div>

点击注销后跳转到登陆页面:
在这里插入图片描述

导入thymeleaf和springsecurity整合包:

<dependency>
  <groupId>org.thymeleaf.extras</groupId>
  <artifactId>thymeleaf-extras-springsecurity5</artifactId>
  <version>3.0.4.RELEASE</version>
</dependency>

改造index:

springsecurity命名空间:

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
>
<body>
  <!--主容器-->
  <div class="ui container">

    <div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
      <div class="ui secondary menu">
        <a class="item"  th:href="@{/index}">首页</a>
        <!--登录注销-->
        <div class="right menu">
          <!--如果未登录-->
          <div sec:authorize="!isAuthenticated()">
            <a class="item" th:href="@{/toLogin}">
              <i class="address card icon"></i> 登录
            </a>
          </div>
          <!--如果已登录-->
          <div sec:authorize="isAuthenticated()">
            <a class="item">
              <i class="address card icon"></i>
              用户名:<span sec:authentication="principal.username"></span>&nbsp;&nbsp;
              角色:<span sec:authentication="principal.authorities"></span>
            </a>
          </div>
          <div sec:authorize="isAuthenticated()">
            <a class="item" th:href="@{/logout}">
              <i class="address card icon"></i> 注销
            </a>
          </div>
        </div>
      </div>
    </div>
    <div class="ui segment" style="text-align: center">
      <h3>Spring Security</h3>
    </div>
    <div>
      <br>
      <div class="ui three column stackable grid">
        <div class="column" sec:authorize="hasRole('vip1')">
          <div class="ui raised segment">
            <div class="ui">
              <div class="content">
                <h5 class="content">Level 1</h5>
                <hr>
                <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
              </div>
            </div>
          </div>
        </div>

        <div class="column" sec:authorize="hasRole('vip2')">
          <div class="ui raised segment">
            <div class="ui">
              <div class="content">
                <h5 class="content">Level 2</h5>
                <hr>
                <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
              </div>
            </div>
          </div>
        </div>

        <div class="column" sec:authorize="hasRole('vip3')">
          <div class="ui raised segment">
            <div class="ui">
              <div class="content">
                <h5 class="content">Level 3</h5>
                <hr>
                <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
              </div>
            </div>
          </div>
        </div>

      </div>
    </div>

  </div>
  <script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
  <script th:src="@{/qinjiang/js/semantic.min.js}"></script>
</body>
</html>

测试,登陆123,实现动态菜单:

在这里插入图片描述

1.8.4 记住我及首页定制
  • 记住我
//开启记住我功能   与下面的name属性匹配
http.rememberMe().rememberMeParameter("remember"); 
<div class="field">
  <input type="checkbox" name="remember"> 记住我
</div>

测试:

在这里插入图片描述

在这里插入图片描述

关闭浏览器后,依然可以跳转到上面的画面!记住我成功!

  • 首页定制

在这里插入图片描述

1.9 Shiro

Apache Shiro 是 Java 的一个安全框架。

在这里插入图片描述

回顾核心API:

  • Subject:用户主体
  • SecurityManager:安全管理器
  • Realm:Shiro 连接数据
1.9.1 Shiro快速开始

导入依赖:

<dependencies>
  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.1</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.21</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.21</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.21</version>
    <scope>test</scope>
  </dependency>
</dependencies>

导入log4j:

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

编写log4j.properties:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
# General Apache libraries
log4j.logger.org.apache=WARN
# Spring
log4j.logger.org.springframework=WARN
# Default Shiro logging
log4j.logger.org.apache.shiro=INFO
# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

创建shiro.ini:

[users]
 #user'root'withpassword'secret'andthe'admin'role
root=secret,admin
 #user'guest'withthepassword'guest'andthe'guest'role
 guest=guest,guest
#user'presidentskroob'withpassword'12345'("That'sthesame combination on
#myluggage!!!";)),androle'president'
presidentskroob=12345,president
#user'darkhelmet'withpassword'ludicrousspeed'androles'darklord'and 'schwartz'  
darkhelmet=ludicrousspeed,darklord,schwartz
 #user'lonestarr'withpassword'vespa'androles'goodguy'and'schwartz'
lonestarr=vespa,goodguy,schwartz
[roles]
 #'admin'rolehasallpermissions,indicatedbythewildcard'*'
admin=*
 #The'schwartz'rolecandoanything(*)withanylightsaber:
schwartz=lightsaber:*
#The'goodguy'roleisallowedto'drive'(action)thewinnebago(type) with
#licenseplate'eagle5'(instancespecificid)
goodguy=winnebago:drive:eagle5

测试QuickStrat.java:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QuickStrat {
    private static final transient Logger log = LoggerFactory.getLogger(QuickStrat.class);
    public static void main(String[] args) {
        Factory<SecurityManager> factory = new
                IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //获取当前的用户的对象
        Subject currentUser = SecurityUtils.getSubject();
        //通过当前用户获取session
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Retrieved the correct value! [" + value + "]");
        }
        //判断当前用户是否被认证
        if (!currentUser.isAuthenticated()) {
            //令牌
            UsernamePasswordToken token = new
                    UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(true);//设置记住我
            try {
                currentUser.login(token);//执行登陆操作
            } catch (UnknownAccountException uae) {  //用户名不存在
                log.info("There is no user with username of " +
                        token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {//密码错误
                log.info("Password for account " +
                        token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) { //账号被锁定
                log.info("The account for username " + token.getPrincipal() + " is locked.  " + "Please contact your administrator to unlock it.");
            } catch (AuthenticationException ae) {//认证异常
                //unexpected condition?  error?
            } }
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
        //测试角色
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }
        //粗粒度
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }
        //细粒度
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
            "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }
        //注销
        currentUser.logout();
        //结束
        System.exit(0);
    }
}

在这里插入图片描述

1.9.2 Spring boot集成Shiro

创建一个Spring boot项目

导入Spring boot web和thymeleaf

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

写一个静态页面:

在这里插入图片描述

写controller:

@Controller
public class MyController {
    @RequestMapping({"/","/index"})
    public String toIndex(Model model){
       model.addAttribute("msg","hello,honey");
       return "index";
    }
}

在这里插入图片描述

导入shiro整合依赖:

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.1</version>
</dependency>

编写config:

package com.candy.config;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

//第三步:创建 ShiroFilterFactoryBean
//第二步:创建 DefaultWebSecurityManager
//第一步:创建 realm 对象 自定义
}
  • 第一步:创建 realm 对象 自定义
package com.candy.config;

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;

//自定义  继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权了");
        return null;
    }
   //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("认证了");
        return null;
    }
}

在config中使用:

@Bean
public UserRealm userRealm(){
  return new UserRealm();
}
  • 第二步:创建 DefaultWebSecurityManager(死代码)
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
  DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  //关联UserRealm
  securityManager.setRealm(userRealm);
  return securityManager;
}
  • 第三步:创建 ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
  ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
  //设置安全管理器
  bean.setSecurityManager(defaultWebSecurityManager);
  return bean;
}

在这里插入图片描述

测试:

创建两个静态页面:

在这里插入图片描述

在controller中添加方法访问:

@RequestMapping("/user/add")
public String add(){
  return "/user/add";
}
@RequestMapping("/user/uodate")
public String update(){
  return "/user/update";
}

在index.html中添加链接:

<a th:href="@{/user/add.html}">add</a>
<a th:href="@{/user/update.html}">update</a>

在这里插入图片描述

1.9.3 实现登陆拦截

在shiroFilter方法中添加属性:

//创建 ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        //添加shiro的内置过滤器
        /*
           anon:无需认证就可以访问
           authc:必须认证了才能访问
           user:必须拥有"记住我"功能才能用
           perms:拥有对某个资源的权限才能访问
           role:拥有某个角色权限才能访问
         */
        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");
        bean.setFilterChainDefinitionMap(filterMap);
        return bean;
    }

访问/user/add被拦截:

在这里插入图片描述

实现拦截后跳转到登陆页面:

创建login.html:

<body>
<h1>login</h1>
<form action="">
    <p>username:<input type="text" name="username"></p>
    <p>password:<input type="text" name="password"></p>
    <p>LOGIN<input type="submit"></p>
</form>
</body>

在controller层写跳转方法:

@RequestMapping("toLogin")
public String toLogin(){
  return "login";
}

最后在拦截之后设置登陆请求:

在这里插入图片描述

1.9.4 实现用户认证

用户认证放在Realm中

先去controller中添加处理登陆过程:

@RequestMapping("/login")
public String login(String username,String password,Model model){
  //获取当前用户
  Subject subject = SecurityUtils.getSubject();
  //封装用户的登陆数据
  UsernamePasswordToken token = new UsernamePasswordToken(username,password);
  try{
    subject.login(token);//执行登陆的方法
    return "index";
  }catch (UnknownAccountException e){//用户名不存在
    model.addAttribute("msg","用户名不存在");
    return "login";
  }catch (IncorrectCredentialsException e){//密码不存在
    model.addAttribute("msg","密码错误");
    return "login";
  }
}

然后去静态页面绑定数据:

在这里插入图片描述

最后去Realm中模拟数据并添加认证:

//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  System.out.println("认证了");
  //用户名、密码
  String name = "root";
  String password = "123";
  UsernamePasswordToken userToken = (UsernamePasswordToken)token;
  if (!userToken.getUsername().equals(name)){
    return null;//抛出异常 UnknownAccountException
  }
  //密码认证 shiro做
  return new SimpleAuthenticationInfo("",password,"");
}

测试:

在这里插入图片描述

1.9.5 整合mybatis

导入依赖:

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.1</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.7</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
</dependency>

新建一个application.yaml:

spring:
  datasource:
    username: root
    password: root12345
    url: jdbc:mysql://localhost:3306/mybts?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
druid:
  #     配置初始化大小、最小、最大线程数
  initialSize: 5
  minIdle: 5
  #     CPU核数+1,也可以大些但不要超过20,数据库加锁时连接过多性能下降
  maxActive: 20
  #     最大等待时间,内网:800,外网:1200(三次握手1s)
  maxWait: 60000
  timeBetweenEvictionRunsMillis: 60000
  #     配置一个连接在池中最大空间时间,单位是毫秒
  minEvictableIdleTimeMillis: 300000
  validationQuery: SELECT 1
  testWhileIdle: true
  #     设置从连接池获取连接时是否检查连接有效性,true检查,false不检查
  testOnBorrow: false
  #     设置从连接池归还连接时是否检查连接有效性,true检查,false不检查
  testOnReturn: false
  #     可以支持PSCache(提升写入、查询效率)
  poolPreparedStatements: true
  #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
  filters: stat,wall,log4j
  #     保持长连接
  keepAlive: true
  maxPoolPreparedStatementPerConnectionSize: 20
  useGlobalDataSourceStat: true
  connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
  type-aliases-package: com.candy.pojo
  mapper-locations:
    - classpath:mapper/*.xml

编写实体类:

package com.candy.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

编写mapper文件:

@Repository
@Mapper
public interface UserMapper {
    public User queryUserByName(String name);
}

编写mapper配置文件:

<?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.candy.mapper.UserMapper">
    <select id="queryUserByName" parameterType="String"
            resultType="com.candy.pojo.User">
        select * from user where name = #{name}
    </select>
</mapper>

编写service层:

public interface UserService {
    public User queryUserByName(String name);
}
@Service
public class UserServiceImpl implements UserService{
    @Autowired
    UserMapper userMapper;
    @Override
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }
}

测试:

@SpringBootTest
class SpbShiroApplicationTests {
    @Autowired
    UserServiceImpl userService;

    @Test
    void contextLoads() {
        User user = userService.queryUserByName("时代少年团");
        System.out.println(user);
    }
}

在这里插入图片描述

改造Realm:

//自定义  继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;
    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权了");
        return null;
    }
   //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("认证了");

        UsernamePasswordToken userToken = (UsernamePasswordToken)token;
        //连接数据库
         User  user = userService.queryUserByName(userToken.getUsername());
         if (user==null){
             //用户名不存在
             return null; //shiro底层就会抛出 UnknownAccountException
         }
        //密码认证 shiro做
        return new SimpleAuthenticationInfo("",user.getPwd(),"");
    }
}

测试:

在这里插入图片描述

1.9.6 请求授权实现

设置授权限制(shiroFilter中):

 filterMap.put("/user/add","perms[user:add]");

在这里插入图片描述

拦截成功!

配置未授权页面,添加一个controller方法:

@RequestMapping("/noauth")
@ResponseBody
public String noAuth(){
    return "未经授权不能访问此页面";
}

然后在shiroFilter中设置未授权的跳转:

 bean.setUnauthorizedUrl("/noauth");

在这里插入图片描述

设置授权

 //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权了");
        //给资源进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 
        //添加资源的授权字符串
        info.addStringPermission("user:add");
        return info;
    }

测试:

所有用户都可以访问add接口,授权成功:

在这里插入图片描述

给数据库增加一个字段,存放权限:

在这里插入图片描述
在这里插入图片描述

修改实体类:

在这里插入图片描述

设置授权:

在这里插入图片描述

//自定义  继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;
    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权了");
        //给资源进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //添加资源的授权字符串
        info.addStringPermission("user:add");
        //取当前对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();//拿到当前用户对象
        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());
        return info;
    }
   //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("认证了");

        UsernamePasswordToken userToken = (UsernamePasswordToken)token;
        //连接数据库
         User  user = userService.queryUserByName(userToken.getUsername());
         if (user==null){
             //用户名不存在
             return null; //shiro底层就会抛出 UnknownAccountException
         }
        //密码认证 shiro做
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}

在这里插入图片描述

1.9.7 整合thymeleaf

导入依赖:

 <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency> 

改造静态页面,导入shiro命名空间:

<html lang="en" xmlns:th="http://www.thymeleaf.org" 
			          xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
</html>
<body>
  <h1>Home</h1>
 <p th:if="${session.loginUser==null}">
   <a th:href="@{/toLogin}">登录</a>
  </p>
  <p th:text="${msg}"></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>

测试:

在这里插入图片描述

为了完美,我们在用户登录后应该把信息放到Session中,我们完善下!在执行认证逻辑时候,加入session

(在Realm中):

Subject  subject = SecurityUtils.getSubject();
subject.getSession().setAttribute("loginUser",user);

在这里插入图片描述

1.10 Swagger

1.10.1 swagger介绍与集成

前后端分离:vue+springboot

后端:控制、服务层、数据访问层

前端:控制层、视图层

前后端如何交互:API

swagger号称世界上最流行的API框架,API文档与API定义同步更新,直接运行,支持多种语言。

在项目中使用swagger需要Springfox(2.x版本):

  • swagger2
  • swagger-ui

在这里插入图片描述

Spring boot集成swagger

导入相关依赖:

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger2</artifactId>
  <version>3.0.0</version>
</dependency>
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>3.0.0</version>
</dependency>

swagger3.0:

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-boot-starter</artifactId>
  <version>3.0.0</version>
</dependency>

编写controller:

@RestController
public class HelloController {
    @RequestMapping(value = "/hello")
    public String hello(){
        return "hello";
    }
}

编写application.yaml:

spring:
  application:
    name: spb-sw
server:
  port: 8001
# ===== 自定义swagger配置 ===== #
swagger:
  enable: true
  application-name: ${spring.application.name}
  application-version: 1.0
  application-description: springfox swagger 3.0
  try-host: http://localhost:${server.port}

测试:

在这里插入图片描述

使用**@EnableOpenApi**注解,启用swagger配置:

@Configuration
@EnableOpenApi
public class SwaggerConfig {

}

访问地址:http://localhost:端口号/swagger-ui/index.html,(注意swagger2.x版本中访问的地址的为http://localhost:端口号/swagger-ui.html)

测试:

在这里插入图片描述

1.10.2 配置swagger信息

首先编写SwaggerProperties去获取application.yaml中swagger的配置信息:

package com.candy;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties("swagger")
public class SwaggerProperties {
    /**
     * 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
     */
    private Boolean enable;
    private String applicationName;
    private String applicationVersion;
    private String applicationDescription;
    /**
     * 接口调试地址
     */
    private String tryHost;

    public Boolean getEnable() {
        return enable;
    }
    public void setEnable(Boolean enable) {
        this.enable = enable;
    }
    public String getApplicationName() {
        return applicationName;
    }
    public void setApplicationName(String applicationName) {
        this.applicationName = applicationName;
    }
    public String getApplicationVersion() {
        return applicationVersion;
    }
    public void setApplicationVersion(String applicationVersion) {
        this.applicationVersion = applicationVersion;
    }
    public String getApplicationDescription() {
        return applicationDescription;
    }
    public void setApplicationDescription(String applicationDescription) {
        this.applicationDescription = applicationDescription;
    }
    public String getTryHost() {
        return tryHost;
    }
    public void setTryHost(String tryHost) {
        this.tryHost = tryHost;
    }
}

然后在写swaggerconfiguration:

package com.candy.config;
import com.candy.SwaggerProperties;
import io.swagger.models.auth.In;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.*;

@Configuration
@EnableOpenApi
public class SwaggerConfiguration {
    private final SwaggerProperties swaggerProperties;

    public SwaggerConfiguration(SwaggerProperties swaggerProperties) {
        this.swaggerProperties = swaggerProperties;
    }
    @Bean
    public Docket createReatApi(){
        return new Docket(DocumentationType.OAS_30).pathMapping("/")
                //定义是否开启swagger
                .enable(swaggerProperties.getEnable())
                //将api的元信息设置为包含json ResourceListing响应中
                .apiInfo(apiInfo())
                //设置接口调试地址
                .host(swaggerProperties.getTryHost())
                //选择哪些接口作为swagger的doc发布
                .select()
                //配置要扫描接口的方式 any:全部 basePackage:指定包
                .apis(RequestHandlerSelectors.basePackage("com.candy"))
                //过滤路径
                .paths(PathSelectors.any())
                .build()
                //支持的通讯协议集合
                .protocols(newHashSet("https","http"))
                //授权信息设置,必要的header token等认证信息
                .securitySchemes(securitySchemes())
                //授权信息全局应用
                .securityContexts(securityContexts());


    }
    //api页面上半部分展示信息
    private ApiInfo apiInfo(){
        return new ApiInfoBuilder().title(swaggerProperties.getApplicationName()+"ApiDoc")
                .description(swaggerProperties.getApplicationDescription())
                 //作者信息
                .contact(new Contact("sixcandy","null","1161187988@qq.com"))
                .version("application version:"+swaggerProperties.getApplicationVersion())
                .build();
    }
    private Set<String> newHashSet(String type1,String type2){
        Set<String> set = new HashSet<>();
        set.add(type1);
        set.add(type2);
        return set;
    }
    private List<SecurityScheme> securitySchemes(){
         ApiKey apiKey = new ApiKey("BASE_TOKEN","token", In.HEADER.toValue());
        return Collections.singletonList(apiKey);
    }
    private List<SecurityContext> securityContexts(){
        return Collections.singletonList(
                SecurityContext.builder()
                        .securityReferences(Collections.singletonList(new SecurityReference("BASE_TOKEN",new AuthorizationScope[]{new AuthorizationScope("global","")} )))
                        .build()
        );
    }
}

测试:

在这里插入图片描述

给api文档写注释:

添加一个user类:

package com.candy.pojo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel("用户实体类")
public class User {
    @ApiModelProperty("用户名")
    public String username;
    @ApiModelProperty("密码")
    public String password;
}

改造controller:

@RestController
public class HelloController {
    @GetMapping (value = "/hello")
    public String hello(){
        return "hello";
    }
    @PostMapping("/user")
    public User user(){
        return new User();
    }
}

测试:

在这里插入图片描述
完结撒花!!!芜湖!!!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
整理自尚硅谷视频教程springboot高级篇,并增加部分springboot2.x的内容 一、Spring Boot与缓存 一、JSR107 Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。 • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可 以在运行 期访问多个CachingProvider。 • CacheManager定义了创建、配置、获取、管理和控制多个唯一命名 的Cache,这些Cache 存在于CacheManager的上下文中。一个CacheManager仅被一个 CachingProvider所拥有。 • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个 Cache仅被一个 CacheManager所拥有。 • Entry是一个存储在Cache中的key-value对。 • Expiry 每一 个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期 的状态。一旦过期,条 目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。 二、Spring缓存抽象 Spring从3.1开始定义了org.springframework.cache.Cache 和 org.springframework.cache.CacheManager接口来统一不同的缓存技术; 并支持使用JCache(JSR- 107)注解简化我们开发; • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合; • Cache接 口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache 等; • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否 已经被调用 过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法 并缓存结果后返回给用户。下 次调用直接从缓存中获取。 • 使用Spring缓存抽象时我们需要关注以下两点; 1、确定方法需要被缓存 以及他们的缓存策略 2、从缓存中读取之前缓存存储的数据 Cache 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、 ConcurrentMapCache等 CacheManager 缓存管理器,管理各种缓存(Cache)组件 @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @CacheEvict 清空缓存 @CachePut 保证方法被调用,又希望结果被缓存。 @EnableCaching 开启基于注解的缓存 keyGenerator 缓存数据时key生成策略 serialize 缓存数据时value序列化策略 @CacheConfig 抽取缓存的公共配置 三、几个重要概念&缓存注解 1、常用注解 2、常用参数 名字 位置 描述 示例 methodName root object 当前被调用的方法名 #root.methodName method root object 当前被调用的方法 #root.method.name target root object 当前被调用的目标对象 #root.target targetClass root object 当前被调用的目标对象类 #root.targetClass args root object 当前被调用的方法的参数列表 #root.args[0] 3、常用参数SPEL说明 名字 位置 描述 示例 caches root object 当前方法调用使用的缓存列表(如 @Cacheable(value= {"cache1","cache2"}) ), 则有两 个cache #root.caches[0].name argument name evaluation context 方法参数的名字. 可以直接 #参数 名 ,也可以使用 #p0或#a0 的形 式,0代表参数的索引; #iban 、 #a0 、 #p0 result evaluation context 方法执行后的返回值(仅当方法执 行之后的判断有效,如‘unless’ , ’cache put’的表达式 ’cache evict’的表达式 beforeInvocation=false ) #result 四、代码中使用缓存 1、搭建基本环境 1、导入数据库文件 创建出department和employee表 2、创建javaBean封装数据 3、整合MyBatis操作数据库 1.配置数据源信息 2.使用注解版的MyBatis; 1)、@MapperScan指定需要扫描的mapper接口所在的包
### 回答1: 很抱歉,我是AI语言模型,无法回答关于“狂神说springsecurity笔记”的问题。但我可以告诉你,Spring Security是一个基于Spring框架的安全框架,用于保护Web应用程序。它提供了身份验证、授权、攻击防护等功能,可以帮助开发人员构建安全可靠的Web应用程序。狂神是一位知名的Java技术博主,他在自己的博客和视频中分享了很多关于Spring Security的知识和经验。如果你想学习Spring Security,可以参考他的笔记和教程。 ### 回答2: Spring Security 是一个强大的开源安全框架,用于保护 Spring 应用程序。它提供了一系列的安全服务,例如身份验证(Authentication)、授权(Authorization)、密码加密、会话管理等。 狂神在他的 Spring Security 笔记中详细介绍了该框架的使用方式和相关概念。首先,他解释了身份验证的重要性,提及了不同的身份验证方法,如基于表单的认证、基于记住我(Remember Me)的认证以及基于 HTTP Basic 和 Digest 验证等。他还介绍了如何自定义认证过程和处理认证失败的方法。 其次,狂神介绍了授权的概念和不同的授权方式。他解释了基于角色(Role)的访问控制和基于资源(Resource)的访问控制,并示范了如何在 Spring Security 中实现这些授权方式。他还介绍了如何使用注解和表达式来简化授权配置。 此外,狂神还介绍了 Spring Security 提供的其他功能,如密码加密、会话管理、防止跨站点请求伪造(CSRF)攻击等。他指导读者如何使用这些功能来增强应用程序的安全性。 总的来说,狂神的 Spring Security 笔记对于初学者来说是一个很好的入门指南。他提供了丰富的示例代码和详细解释,让读者可以很容易地理解和使用 Spring Security。无论是开发 Web 应用程序还是企业级应用程序,掌握 Spring Security 都是非常重要的,狂神的笔记提供了一个很好的起点。 ### 回答3: spring security 是一个开源的安全框架,可以为应用程序提供身份验证和授权管理的功能。它基于 JavaEE 标准认证和授权机制,并提供了一套更灵活的安全性管理机制。 首先,Spring Security 实现了用户认证的功能,可以通过多种认证方式来验证用户的身份,包括基于数据库的认证、LDAP 认证、OAuth2 认证等。可以根据具体的需求选择使用不同的认证方式。 其次,Spring Security 提供了授权管理的功能,可以设置不同的权限角色,对不同的用户进行授权。可以通过配置的方式进行权限的控制,通过注解或者编程的方式进行细粒度的控制,可以灵活地满足不同的应用需求。 另外,Spring Security 还提供了许多其他的安全支持功能,如防止 CSRF 攻击、防止点击劫持、防止 SQL 注入等。它还支持与其他框架的集成,如与 Spring BootSpring MVC 的集成,可以方便地在现有的应用中集成安全功能。 Spring Security 的架构清晰,易于理解和使用。它的设计模式和扩展机制可以满足不同级别的定制需求,使得我们可以根据实际情况来灵活地进行使用和扩展。同时,Spring Security 还提供了丰富的文档和示例代码,方便开发者学习和使用。 总之,Spring Security 是一个功能强大且灵活的安全框架,可以在应用程序中实现身份验证和授权管理的功能。通过使用 Spring Security,我们可以有效地保护和管理我们的应用程序,提升系统的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值