个人博客

http://www.milovetingting.cn

第一个SpringBoot程序

1、创建项目

选择Spring Initializr,点击Next

image-20230313220513154

2、设置项目

image-20230313220604312

3、选择依赖

image-20230313220650087

4、填写项目信息

image-20230313220749435

5、创建controller

FooController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.wangyz.springboot.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/foo")
public class FooController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
}

6、运行项目

image-20230313221122360

7、在浏览器查看

image-20230313221209639

修改默认启动banner

Resources目录下新建banner.txt文件,内容随意,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////

启动后,可以看到已经修改

image-20230313223032360

YAML

基本数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# key-value
name: zs

# 对象
person:
name: zs
age: 18

# 对象-行内写法
student: {name: zs,age: 18}

# 数组
aninals:
- dog
- cat

# 数组-行内写法
pets: [dog,cat]

相对于properties只能保存key-value,yaml可以保存更多的数据类型

propertiesyaml中同时配置,properties的优先级要高。

给实体赋值

1、使用默认的配置文件:application.propertiesapplication.yaml

1
2
3
person:
name: ls
age: 18
1
2
person.name=zs
person.age=18

person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.wangyz.springboot.pojo;

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

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private int age;

public Person() {
}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

SpringbootApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.wangyz.springboot;

import com.wangyz.springboot.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootApplicationTests {

@Autowired
private Person person;

@Test
void contextLoads() {
System.out.println(person);
}

}

2、自定义配置文件

person.properties

1
2
person.name=ww
person.age=18

person.java

1
2
3
4
5
6
7
8
@Component
//指定配置文件
@PropertySource(value = "classpath:person.properties")
public class Person {
@Value("${person.name}")
private String name;
private int age;
}

JSR303校验

1、依赖引入

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.1.Final</version>
</dependency>

<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.0</version>
</dependency>

2、增加注解

Person.java

1
2
3
4
5
6
@Validated
public class Person {
@Email(message = "邮箱格式不正确")
@Value("${person.email}")
private String email;
}

多环境配置

优先级

1、项目根目录下的config文件夹下的配置文件

2、项目根目录下的配置文件

3、Resource目录下的config文件夹下的配置文件

4、Resource目录下的配置文件

多环境

1、通过properties设置

application-dev.properties

1
server.port=8081

application-test.properties

1
server.port=8082

application.properties选择环境

1
spring.profiles.active=test

2、通过yaml设置

application.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 8084
spring:
profiles:
active: test

---
server:
port: 8085
spring:
profiles: dev

---
server:
port: 8086
spring:
profiles: test

静态资源配置

优先级

resources>static>public

自定义资源路径

1
2
spring.mvc.static-path-pattern=/foo/**
spring.web.resources.static-locations=classpath:/foo

自定义首页

index.html可以放在resources目录下的public,resources,static目录下

Thymeleaf

1、导入依赖

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>

2、配置

1
2
3
4
5
6
7
8
9
#关闭Thymeleaf的缓存,开发过程中无需重启
spring.thymeleaf.cache = false
#设置thymeleaf页面的编码
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#设置thymeleaf页面的后缀
spring.thymeleaf.suffix=.html
#设置thymeleaf页面的存储路径
spring.thymeleaf.prefix=classpath:/templates/

自定义视图解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.wangyz.springboot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Locale;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

@Bean
public ViewResolver myViewResolver() {
return new MyViewResolver();
}


public static class MyViewResolver implements ViewResolver {

@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}
}

视图控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.wangyz.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

/**
* 视图控制器
*
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/thymeleaf/hello").setViewName("index");
}
}

JDBC

1、依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>

2、配置

application.yml

1
2
3
4
5
6
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver

3、使用
JDBCController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.wangyz.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
public class JDBCController {
@Autowired(required = false)
JdbcTemplate template;

@GetMapping("/addUser")
public String addUser() {
String sql = "insert into mybatis.user(id,name,pwd) values(5,'hello','123456')";
template.update(sql);
return "add success!";
}

@GetMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable("id") int id) {
String sql = "delete from mybatis.user where id=?";
Object[] params = new Object[1];
params[0] = id;
template.update(sql, params);
return "delete success!";
}

@GetMapping("/updateUser/{id}")
public String updateUser(@PathVariable("id") int id) {
String sql = "update mybatis.user set name=?,pwd=? where id=?";
Object[] params = new Object[3];
params[0] = "小明";
params[1] = "123456";
params[2] = id;
template.update(sql, params);
return "update success!";
}

@GetMapping("/userList")
public List<Map<String, Object>> getUserList() {
String sql = "select * from mybatis.user";
List<Map<String, Object>> list = template.queryForList(sql);
return list;
}
}

Druid

1、依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>

2、配置YAML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource

initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true

filters: stat
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionoProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3、Config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.wangyz.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@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> initParam = new HashMap<>();
//登录用户名
initParam.put("loginUsername","admin");
//登录密码
initParam.put("loginPassword","123456");
//允许访问
initParam.put("allow","");

bean.setInitParameters(initParam);
return bean;
}

@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map<String,String> initParam = new HashMap<>();
initParam.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParam);
return bean;
}
}

4、访问

http://localhost:8080/druid/index.html

MyBatis

1、依赖

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

2、pojo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.wangyz.demo.pojo;

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

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

3、mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.wangyz.demo.mapper;

import com.wangyz.demo.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface UserMapper {

int addUser(User user);

int deleteUser(int id);

int updateUser(User user);

User getUserById(int id);

List<User> getUserList();

}

4、mapper.xml

resource/mybatis/mapper/UserMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangyz.demo.mapper.UserMapper">

<insert id="addUser" parameterType="User">
insert into mybatis.user (id,name,pwd) values(#{id},#{name},#{pwd})
</insert>

<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id}
</delete>

<update id="updateUser" parameterType="User">
update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id}
</update>

<select id="getUserById" resultType="User">
select * from mybatis.user where id = #{id}
</select>

<select id="getUserList" resultType="User">
select * from mybatis.user
</select>
</mapper>

5、配置

application.properties

1
2
mybatis.type-aliases-package=com.wangyz.demo.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

Spring Security

1、依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

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

2、controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.wangyz.demo.controller;

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

@Controller
public class SecurityController {

@RequestMapping("/")
public String index() {
return "index.html";
}

@RequestMapping("/l1")
public String l1() {
return "/l1/index.html";
}

@RequestMapping("/l2")
public String l2() {
return "/l2/index.html";
}

@RequestMapping("/l3")
public String l3() {
return "/l3/index.html";
}

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

@RequestMapping("/toLogout")
public String logout(){
return "logout.html";
}
}

3、config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.wangyz.demo.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/l1/**").hasRole("v1")
.antMatchers("/l2/**").hasRole("v2")
.antMatchers("/l3/**").hasRole("v3");

//开启登录功能,并使用自定义的登录页面
//loginPage:登录的页面
//loginProcessingUrl:处理登录请求的页面
//usernameParameter:设置登录表单的用户名字段name
//passwordParameter:设置登录表单的密码字段name
http.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password");

//开启注销功能,退出成功后跳转/,路由:/logout
http.logout().logoutSuccessUrl("/");

//开启记住我功能
//rememberMeParameter:设置记住我的name
http.rememberMe().rememberMeParameter("remember");
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("v1", "v2", "v3")
.and()
.withUser("u1").password(new BCryptPasswordEncoder().encode("123456")).roles("v1")
.and()
.withUser("u2").password(new BCryptPasswordEncoder().encode("123456")).roles("v2")
.and()
.withUser("u3").password(new BCryptPasswordEncoder().encode("123456")).roles("v3")
;
//连接数据库验证权限
/*User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
auth.jdbcAuthentication().dataSource(datasource).withDefaultSchema()
.withUser(userBuilder.username("user").password("password").roles("USER"))
.withUser(userBuilder.username("admin").password("password").roles("ADMIN"));*/
}
}

4、index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<!--security自带退出登录的路由-->
<div sec:authorize="isAuthenticated()">
<label sec:authentication="name"></label>
<label sec:authentication="principal.authorities"></label>
<a th:href="@{/toLogout}">退出</a>
</div>

<div sec:authorize="!isAuthenticated()">
<a th:href="@{/toLogin}">登录</a>
</div>

<a th:href="@{/l1}">l1</a><br/>
<a th:href="@{/l2}">l2</a><br/>
<a th:href="@{/l3}">l3</a><br/>
</body>
</html>

5、login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Login</title>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link th:href="@{/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">

<main class="form-signin w-100 m-auto">
<form th:action="@{/login}" method="post">
<img class="mb-4" th:src="@{/img/bootstrap-logo.svg}" alt="" width="72" height="57">
<h1 class="h3 mb-3 fw-normal" text="请登录"></h1>

<div class="form-floating">
<input type="text" class="form-control" id="floatingInput" th:placeholder="用户名" name="username">
<label for="floatingInput" th:text="用户名"></label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="floatingPassword" th:placeholder="密码" name="password">
<label for="floatingPassword" th:text="密码"></label>
</div>

<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" th:text="记住我" name="remember">
</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit" th:text="登录"></button>
<p class="mt-5 mb-3 text-muted">&copy; 2017–2022</p>
</form>
</main>

</body>
</html>

6、logout.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Logout</title>
</head>
<body>
<form th:action="@{/logout}" method="post">
<input type="submit" value="退出">
</form>
</body>
</html>

Shiro

1、配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>

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

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

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>

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

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

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

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

2、Config

UserRealm.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.wangyz.demo.config;

import com.wangyz.demo.pojo.User;
import com.wangyz.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {

@Autowired
UserService service;

//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行=>doGetAuthorizationInfo");

Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission(user.getPerm());

return info;
}

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行=>doGetAuthenticationInfo");

UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

User user = service.getUserByName(token.getUsername());

if (user == null) {
return null;
}

return new SimpleAuthenticationInfo(user, user.getPwd(), "");
}
}

ShiroConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.wangyz.demo.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
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
public class ShiroConfig {

@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getWebSecurityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

bean.setSecurityManager(securityManager);

Map<String,String> map = new LinkedHashMap<>();

//设置权限
map.put("/user/add","perms[user:add]");
map.put("/user/update","perms[user:update]");

bean.setFilterChainDefinitionMap(map);

//登录页面
bean.setLoginUrl("/user/login");

//未授权页面
bean.setUnauthorizedUrl("/user/unauthorized");

return bean;
}

@Bean
public DefaultWebSecurityManager getWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

securityManager.setRealm(userRealm);

return securityManager;
}

@Bean
public UserRealm userRealm() {
return new UserRealm();
}

@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}

3、Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.wangyz.demo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class WebController {

@RequestMapping("/")
public String index(Model model) {
model.addAttribute("msg", "hello,shiro");
return "index.html";
}

@RequestMapping("/user/add")
public String add() {
return "/user/add.html";
}

@RequestMapping("/user/update")
public String update() {
return "/user/update.html";
}

@GetMapping("/user/login")
public String login() {
return "login.html";
}

@PostMapping("/user/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.html";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "用户名错误");
return "login.html";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
return "login.html";
}
}

@RequestMapping("/user/unauthorized")
@ResponseBody
public String unauthorized() {
return "未授权,无法访问";
}
}

其它mybatis相关配置略过。

Swagger

1、依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>

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

高版本的springboot会报错,因此降级到2.5.2

2、config

1
2
3
4
5
6
7
8
9
10
11
package com.wangyz.demo.config;

import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

}

3、controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.wangyz.demo.controller;

import com.wangyz.demo.pojo.User;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {

@GetMapping("/hello")
public String hello(){
return "hello";
}

//返回值中存在实体类,就会被扫描到swagger中
//给方法加注释
@ApiOperation("返回用户信息")
@PostMapping("/user")
public User user(User user){
return user;
}

}

4、运行

运行后,在浏览器地址栏输入:http://localhost:8080/swagger-ui.html

5、具体配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.wangyz.demo.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

@Bean
public Docket docket(@Qualifier("apiInfo") ApiInfo apiInfo, Environment environment) {
//设置要显示swagger的环境
Profiles profiles = Profiles.of("dev");
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.enable(flag)//是否开启swagger
.groupName("hello")//分组
.select()
.apis(RequestHandlerSelectors.basePackage("com.wangyz.demo.controller"))
.paths(PathSelectors.ant("/hello/**"))
.build();
}

@Bean
public Docket docketFoo(@Qualifier("apiInfo") ApiInfo apiInfo, Environment environment) {
//设置要显示swagger的环境
Profiles profiles = Profiles.of("dev");
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.enable(flag)//是否开启swagger
.groupName("foo")//分组
.select()
.apis(RequestHandlerSelectors.basePackage("com.wangyz.demo.controller"))
.paths(PathSelectors.ant("/foo/**"))
.build();
}

@Bean
public ApiInfo apiInfo() {
Contact contact = new Contact("milovetingting", "https://www.milovetingting.cn", "milovetingting@gmail.com");
return new ApiInfo("Api Document", "Api Document description", "v1.0", "https://www.milovetingting.cn", contact, "lisense", "licenseUrl", new ArrayList<>());
}
}

异步任务

增加EnableAsync注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.wangyz.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

}

增加Async注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.wangyz.demo.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {

@Async
public void hello(){
try {
Thread.sleep(3000);
}
catch (Exception e){

}
System.out.println("loading");
}
}

定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.wangyz.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.wangyz.demo.service;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class ScheduleService {

//秒 分 时 日 月 年 星期
@Scheduled(cron = "0 * * * * 0-7")
public void hello(){
System.out.println("hello");
}

}

Dubbo-ZooKeeper

配置ZooKeeper

https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.7.1/apache-zookeeper-3.7.1-bin.tar.gz

下载完成后解压到相应的目录下

在解压后的bin目录下,zkServer.cmd用于启动服务端,zkCli.cmd用于启动客户端,双击可运行。

配置Dubbo

1、下载源码

https://github.com/apache/dubbo-admin/tree/master

通过git clone或者通过下载压缩包

2、编译

进行dubbo目录,执行

1
mvn clean package -Dmaven.test.skip=true

执行时,会下载需要的依赖,这个过程需要花费一些时间。

执行完成后,会生成target目录

3、运行

1
java -jar dubbo-admin-server-0.5.0.jar

可能出现的错误:8080端口已被占用

解决方案:

  • 查看被占用端口信息

    1
    netstat -aon|findstr "8080"

    结果:

    1
    TCP		0.0.0.0:8080	0.0.0.0		LISTENING	10648

    最后一列的10648就是占用8080端口的进程。在进程管理器中找到该进程,结束进程。

zookeeper 3.5.5版本中包含一个AdminServer默认的端口是8080,所以导致占用。

解决办法:修改zookeeper默认配置

1
admin.serverPort=8888

访问http://localhost:8080/,默认用户名密码都是root

Provider

1、依赖

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>3.2.0-beta.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>3.2.0-beta.4</version>
<type>pom</type>
</dependency>

<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>3.2.0-beta.4</version>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>

2、配置

application.properties

1
2
3
4
5
6
7
8
server.port=8001

# 服务应用名称
dubbo.application.name=provider
# 注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 扫描包
dubbo.scan.base-packages=com.wangyz.provider.service

3、Service

TicketService.java

1
2
3
4
5
6
package com.wangyz.provider.service;

public interface TicketService {
public String getTicket();
}

TicketServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
package com.wangyz.provider.service;

import org.apache.dubbo.config.annotation.DubboService;

@DubboService
public class TicketServiceImpl implements TicketService {
@Override
public String getTicket() {
return "a ticket from provider!";
}
}

Consumer

1、依赖

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>3.2.0-beta.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>3.2.0-beta.4</version>
<type>pom</type>
</dependency>

<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>3.2.0-beta.4</version>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>

2、配置

application.properties

1
2
3
4
5
6
server.port=8002

dubbo.application.name=consumer

dubbo.registry.address=zookeeper://127.0.0.1:2181

3、Service

将之前Provider端的TicketService.java接口定义复制过来,保证包名和类名路径一致。

UserService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.wangyz.consumer.service;

import com.wangyz.provider.service.TicketService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

@Service
public class UserService {

@DubboReference
TicketService ticketService;

public String getTicket(){
return ticketService.getTicket();
}
}

测试

ConsumerApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.wangyz.consumer;

import com.wangyz.consumer.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ConsumerApplicationTests {

@Autowired
UserService service;

@Test
void contextLoads() {
System.out.println(service.getTicket());
}

}

1、启动Zookeeper

2、启动Provider

3、测试