一日一句:“做什么事都很难 大不了从头再来吧”
微服务阶段
javase:OOP
mysql:持久化
html+css+js+jquery+框架:视图,框架不熟练,css不好;
Javaweb:独立开发MVC三层架构的网站:原始
ssm:框架:简化了我们的开发流程,配置也开始较为复杂;
以上都是打war:外置tomcat运行
spring再简化:springboot-jar:内嵌tomcat;微服务架构!
服务越来越多:springcloud;
什么是微服务?
- 原文地址:http://martinfowler.com/articles/microservices.html
- 翻译:https://www.cnblogs.com/liuning8023/p/443156.html
什么是spring?
什么是springboot?
约定大于配置
springboot的优点
第一个springboot程序
去spring.io官网
第一个springboot程序
springboot原理初探
@SpringBootApplication
结论
run()方法
SpringBoot的配置文件以及自动装配原理
yaml对空格的要求十分高**
yaml可以直接给实体类赋值
JSR303数据校验
@Validated //数据校验
多环境配置以及配置文件位置
application.yml
server:
port: 8001
spring:
profiles:
active: dev
---
server:
port: 8002
spring:
profiles: dev
---
server:
port: 8003
spring:
profiles: test
application.yml
# 配置文件到底能写什么? ---联系---spring.factories
# 在我们这配置文件中能配置的东西,都存在一个固有的规律
# xxxconfiguration xxxproperties 和 配置文件绑定,我们就可以使用自定义的配置了!
# 可以通过 来查看,那些自动类生效,那些没有生效
debug: true
SpringBoot Web开发
jar:webapp!
自动装配
1.创建应用,选择模块!
静态资源
什么是webjars?
优先级:resources>static>public
public:一般存放公共文件js
static:照片啊之类的(默认)
resources:上传的文件
首页定制
创建一个IndexController api接口,来跳转访问index首页
package com.liang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//在templates目录下的所有页面,只能通过controller来跳转
//直接跳转访问这个需要模板引擎的支持! 没有引入就报500 需要引入template的依赖
@Controller
public class IndexController {
@RequestMapping("/x")
public String index(){
return "index";
}
}
#
访问默认图标设置
复制一个图片改名字为favicon.ico放到static里面,清楚浏览器缓存
访问就ok了!
模板引擎
就是之前的jsp
首先导入thymeleaf依赖
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
controller代码
package com.liang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//在templates目录下的所有页面,只能通过controller来跳转
//直接跳转访问这个需要模板引擎的支持! 没有引入就报500 需要引入template的依赖
@Controller
public class IndexController {
@RequestMapping("/x")
public String index(){
return "test";
}
}
想要使用thymeleaf模板,首先导入命令空间
xmlns:th="http://www.thymeleaf.org"
<html lang="en" xmlns:th="http://www.thymeleaf.org">
test.html代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--所以的html元素,都可以被thymeleaf替换接管: th:元素名-->
<h1>thymeleaf模板引擎你好</h1>
<h1 th:text="${msg}"></h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--所以的html元素,都可以被thymeleaf替换接管: th:元素名-->
<div th:text="${msg}"></div>
<div th:utext="${msg}"></div><!--不转义-->
<hr>
<!--<h3 th:each="user:${users}">[[${user}]]</h3>-->
<h3 th:each="user:${users}" th:text="${user}"></h3>
</body>
</html>
springmvc的配置原理
官方文档地址:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-developing-web-applications.html
自己定义一个MyMvcConfig
package com.liang.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;
/*
如果,你想diy一些定制化的功能,只要写这个组件,然后把它交给springboot,springboot
springboot就会帮我们自动装配
*/
//拓展 springmvc dispatcherServlet
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
// public interface ViewResolver 实现了视图解析器接口的类,我们就可以把它看做视图解析器
//自定义一个自己的视图解析器MyViewResolver 静态内部类
public static class MyViewResolver implements ViewResolver{
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
}
@EnableWebMvc //这玩意导入一个类:DelegatingWebMvcConfiguration:从容器中获取所有的WebMvcconfig
thymeleaf常用命名空间
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
整合JDBC
报了市区异常
application.yml
spring:
datasource:
username: root
password: root
#加入时区报错了,就增加一个时区ok了&serverTimezone=UTC&
url: jdbc:mysql://localhost:3306/mybatis?&serverTimezone=UTC&useUnicode=false&charactEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
查询数据库
JDBCController
package com.liang.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 //这个是mvc的注解
public class JDBCController {
@Autowired
JdbcTemplate jdbcTemplate;
//查询数据库的所有信息
//没有实体类,数据库中的东西,怎么获取?map
@GetMapping("userList")
public List<Map<String,Object>> userList(){
String sql = "select * from user";
List<Map<String, Object>> map = jdbcTemplate.queryForList(sql);
return map;
}
@GetMapping("/addUser")
public String addUser(){
String sql="insert into mybatis.user(id,name,pwd) values (1,'平面','123123')";
jdbcTemplate.update(sql);//这个帮我们做了事务
return "update-ok!!!";
}
@GetMapping("/updateUser/{id}")
public String updateUser(@PathVariable("id") int id){
String sql = "update mybatis.user set name=?,pwd=? where id="+id;
//封装
Object[] objects = new Object[2];
objects[0]="小明22";
objects[1]="lll";
jdbcTemplate.update(sql,objects);
return "updateUser-ok";
}
@GetMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable("id") int id){
String sql="delete from mybatis.user where id=?";
jdbcTemplate.update(sql,id);
return "deleteUser-ok!!";
}
}
整合DRUID数据源
导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.1</version>
</dependency>
application.yml
spring:
datasource:
username: root
password: root
#加入时区报错了,就增加一个时区ok了?&serverTimezone=UTC&
url: jdbc:mysql://localhost:3306/mybatis?&serverTimezone=UTC&useUnicode=false&charactEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #配置数据源
#SpringBoot默认是不注入这些的,需要自己绑定
#druid数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity
#则导入log4j 依赖就行
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
<!--导入log4j日志依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
DruidConfig德鲁伊监视器配置文件
package com.liang.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;
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控:web.xml ServletRegistrationBean
//因为springboot内置了servlet容器,所以没有web.xml,替代方法ServletRegistrationBean
@Bean //这个不能漏
public ServletRegistrationBean a(){
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
//后台需要有人登陆,账号密码配置
HashMap<String,String> initParameters = new HashMap<>();
//添加配置
initParameters.put("loginUsername","admin");//登录的key,是固定的 loginUsername loginPassword
initParameters.put("loginPassword", "123456");
//允许谁访问
initParameters.put("allow", "");//所有人可以访问
//禁止谁能访问
initParameters.put("liang", "192.168.1212");
bean.setInitParameters(initParameters);//设置初始化参数
return bean;
}
//filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
//可以过滤那些请求?
HashMap<String,String> initParameters = new HashMap<>();
//这些东西不进行统计
initParameters.put("exclusions", "*.js,*.css,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
}
执行一条sql语句后,就可以看到德鲁伊检测器那里有数据了
整合Mybatis
整合包
- 导入包
- 配置文件
- mybatis配置
- 编写sql
- service层调用dao层
- controller调用service层
mybatis-spring-boot-starter
导入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
applica.yml配置文件
spring:
datasource:
username: root
password: root
#加入时区报错了,就增加一个时区ok了&serverTimezone=UTC&
url: jdbc:mysql://localhost:3306/mybatis?&serverTimezone=UTC&useUnicode=false&charactEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
#整合mybatis
mybatis:
type-aliases-package: com.liang.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
测试是否连接数据库成功
在idea连接数据库
建一个pojo实体类包,建user实体类
package com.liang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
@MapperScan("com.liang.mapper")//扫描所以mapper下面的类
建一个mapper包,UserMapper接口
package com.liang.mapper;
import com.liang.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
//这个注解表示了这是一个mybatis的mapper类 Dao
@Mapper
@Repository //把这个类注入bean
public interface UserMapper {
List<User> queryUserList();
User queryUserById(int id);
int addUser(User user );
int updateUser(User user);
int deleteUser(int id);
}
然后在resources下建一个mybatis包下建一个mapper包建一个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.liang.mapper.UserMapper">
<select id="queryUserList" resultType="User">
select *
from user;
</select>
<select id=" queryUserById" resultType="User">
select * from user where id = #{id}
</select>
<insert id="addUser" parameterType="User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="User">
update user set name =#{name },pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
成功获取数据
UserController
package com.liang.controller;
import com.liang.mapper.UserMapper;
import com.liang.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/queryList")
public List<User> queryUserList(){
List<User> userList = userMapper.queryUserList();
for (User user : userList) {
System.out.println(user);
}
return userList;
}
//添加一个用户
@GetMapping("/addUser")
public String addUser(){
userMapper.addUser(new User(19,"jack","55555"));
return "ok";
}
//修改一个用户
@GetMapping("/updateUser")
public String updateUser(){
userMapper.updateUser(new User(19,"avavva","|777"));
return "ok";
}
//根据id删除用户
@RequestMapping("deleteUser")
public String deleteUser(){
userMapper.deleteUser(19);
return "ok";
}
}
//不需要事务了,mybatis已经配好了
SpringSecurity(安全)环境搭建
在web开发中,安全第一位!过滤器,拦截器~
功能性需求:否
做网站:安全应该在什么时候考虑?设计之初!
- 漏洞,隐私泄露~
- 架构一旦确定~
shiro、SpringSecurity:他们很像,除了类不一样,名字不一样;
认证,授权(vip1,vip2,vip3)
- 功能权限
- 访问权限
- 菜单权限
- …拦截器,过滤器:大量的原生代码~太多冗余代码
新建一个项目
导入thymeleaf依赖
<!--thymeleaf模板-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
这个/不能漏,漏了就访问不到其下面的资源了
RouterController
package com.liang.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;//+id动态访问多个html文件
}
}
用户认证和授权
导入security依赖
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
SecurityConfig固定格式
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
}
授权
设置后就访问不到了,没有权限
认证
SecurityConfig
package com.liang.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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
//AOP方法横切进去: 比拦截器好多了
@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");
//没有权限会到登录页面,需要开启登录的页面
//Login
http.formLogin();
}
//认证,springboot 2.1.x 可以直接使用·
//密码编码
//在spring Secutiry 5.0+增加了很多加密方法~
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常应该从数据库中读
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("liang").password(new BCryptPasswordEncoder().encode("123321")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123321")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123321")).roles("vip1");//roles 是权限 guest:来宾
}
}
注销以及权限控制
//注销,开启了注销功能,跳到首页
//防止网站攻击: get ,post
http.csrf().disable();//关闭csrf功能
http.logout().logoutSuccessUrl("/");
<!--注销-->
<a class="item"th:href="@{/logout}">
<i class="sort numeric upicon"></i> 注销 </a>
导入依赖
<!--security-thymeleaf整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
导入命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf-extras-springsecurity4">
发现不生效,去把maven版本降到2.0.9
<!--菜单根据用户的角色动态实现--><div class="column" sec:suthorize="hsrRole('vip1')">
记住我和首页定制
//开启记住我功能 cookie客户端 默认存两周
cookiedhttp.rememberMe().rememberMeParameter("remember");//接收前端传过来的按钮数据
login.html添加记住我按钮
<div class="field"> //居中
<input type="checkbox" name="remember">记住我</div>
SecurityConfig
package com.liang.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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
//AOP方法横切进去: 比拦截器好多了
@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");
//没有权限会到登录页面,需要开启登录的页面
//Login
//定制登陆页面
http.formLogin().loginPage("/toLogin");
//注销,开启了注销功能,跳到首页
//防止网站攻击: get ,post
http.csrf().disable();//关闭csrf功能
http.logout().logoutSuccessUrl("/");
//开启记住我功能 cookie
http.rememberMe().rememberMeParameter("remember");
}
//认证,springboot 2.1.x 可以直接使用·
//密码编码
//在spring Secutiry 5.0+增加了很多加密方法~
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常应该从数据库中读
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("liang").password(new BCryptPasswordEncoder().encode("123321")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123321")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123321")).roles("vip1");//roles 是权限 guest:来宾
}
}
Shiro
什么是shiro?
新建一个maven项目,shiro快速入门
GitHub代码Shiro地址:https://github.com/apache/shiro/tree/master/samples/quickstart
1.导入依赖
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<!-- configure logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2.配置文件
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' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5
3.helloworld
Quickstart
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Simple Quickstart application showing how to use Shiro's API.
*
* @since 0.9 RC2
*/
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// The easiest way to create a Shiro SecurityManager with configured
// realms, users, roles and permissions is to use the simple INI config.
// We'll do that by using a factory that can ingest a .ini file and
// return a SecurityManager instance:
// Use the shiro.ini file at the root of the classpath
// (file: and url: prefixes load from files and urls respectively):
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
// for this simple example quickstart, make the SecurityManager
// accessible as a JVM singleton. Most applications wouldn't do this
// and instead rely on their container configuration or web.xml for
// webapps. That is outside the scope of this simple quickstart, so
// we'll just do the bare minimum so you can continue to get a feel
// for things.
SecurityUtils.setSecurityManager(securityManager);
// Now that a simple Shiro environment is set up, let's see what you can do:
// get the currently executing user:
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
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 + "]");
}
// let's login the current user so we can check against roles and permissions:
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 more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
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.");
}
//a (very powerful) Instance Level permission:
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!");
}
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
成功打印出日志
D:\JavaEnvironment\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Study software\IntelliJ IDEA 2020.3\lib\idea_rt.jar=53515:D:\Study software\IntelliJ IDEA 2020.3\bin" -Dfile.encoding=UTF-8 -classpath D:\JavaEnvironment\jdk1.8.0_152\jre\lib\charsets.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\deploy.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\dnsns.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\jaccess.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\localedata.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\nashorn.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunec.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\zipfs.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\javaws.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jce.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jfr.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jfxswt.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jsse.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\management-agent.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\plugin.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\resources.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\rt.jar;D:\IdeaWorkSpace2\springboot\springboot-08-shiro\hello-shiro\target\classes;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-core\1.4.1\shiro-core-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-lang\1.4.1\shiro-lang-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-cache\1.4.1\shiro-cache-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-crypto-hash\1.4.1\shiro-crypto-hash-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-crypto-core\1.4.1\shiro-crypto-core-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-crypto-cipher\1.4.1\shiro-crypto-cipher-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-config-core\1.4.1\shiro-config-core-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-config-ogdl\1.4.1\shiro-config-ogdl-1.4.1.jar;C:\Users\Administrator\.m2\repository\commons-beanutils\commons-beanutils\1.9.3\commons-beanutils-1.9.3.jar;C:\Users\Administrator\.m2\repository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;C:\Users\Administrator\.m2\repository\org\apache\shiro\shiro-event\1.4.1\shiro-event-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.21\jcl-over-slf4j-1.7.21.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-log4j12\1.7.21\slf4j-log4j12-1.7.21.jar;C:\Users\Administrator\.m2\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar Quickstart
2021-01-21 14:07:13,605 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler...
2021-01-21 14:07:13,850 INFO [Quickstart] - Retrieved the correct value! [aValue]
2021-01-21 14:07:13,851 INFO [Quickstart] - User [lonestarr] logged in successfully.
2021-01-21 14:07:13,851 INFO [Quickstart] - May the Schwartz be with you!
2021-01-21 14:07:13,851 INFO [Quickstart] - You may use a lightsaber ring. Use it wisely.
2021-01-21 14:07:13,851 INFO [Quickstart] - You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. Here are the keys - have fun!
Shiro的Subject分析
Quickstart官网演示代码
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Simple Quickstart application showing how to use Shiro's API.
*
* @since 0.9 RC2
*/
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// The easiest way to create a Shiro SecurityManager with configured
// realms, users, roles and permissions is to use the simple INI config.
// We'll do that by using a factory that can ingest a .ini file and
// return a SecurityManager instance:
// Use the shiro.ini file at the root of the classpath
// (file: and url: prefixes load from files and urls respectively):
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
// for this simple example quickstart, make the SecurityManager
// accessible as a JVM singleton. Most applications wouldn't do this
// and instead rely on their container configuration or web.xml for
// webapps. That is outside the scope of this simple quickstart, so
// we'll just do the bare minimum so you can continue to get a feel
// for things.
SecurityUtils.setSecurityManager(securityManager);
// Now that a simple Shiro environment is set up, let's see what you can do:
// get the currently executing user:
//获取当前的用户对象 subject
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
//通过当前用户拿到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 + "]");
}
// let's login the current user so we can check against roles and permissions:
//判断当前的用户是否被认证
//token:令牌,没有获取,随机
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 more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
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.");
}
//a (very powerful) Instance Level permission:
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!");
}
//注销
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
springboot整合shiro环境搭建
新建一个moudel springboot工程
<!--
shiro三大对象
subject 用户s
ecurityManager 管理所有用户
Realm 连接数据
-->
导入依赖
<!--shiro整合spring的包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
编写shiro的两个核心配置
ShiroConfig
package com.liang.config;
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;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier( "securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm 对象,需要自定义类 :第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
UserRealm
package com.liang.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;
//自定义的UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=》授权doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了=》认证doGetAuthenticationInfo");
return null;
}
}
MyController
package com.liang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping({"/","index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<hr>
<a th:href="@{/user/add}">add</a> | <a th:href="@{/user/update}">update</a>
</body>
</html>
shiro实现登录拦截
ShiroConfig
package com.liang.config;
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 {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier( "securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证才能访问
user:必须拥有 记住我 功能才能用
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
//登录拦截
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/user/add","authc");
// filterMap.put("/user/update","authc");
filterMap.put("/user/*", "authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录页面
bean.setLoginUrl("/toLogin");
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm 对象,需要自定义类 :第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
MyController
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<form action="">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
shiro实现用户认证
MyController
@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);//执行登录的方法,如果没有异常就说明ok了
return "index";
}catch (UnknownAccountException uae){//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException ice){//密码不存在
model.addAttribute("msg","密码错误");
return "login";
}
}
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
UserRealm
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了=》认证doGetAuthenticationInfo");
//用户名,密码 数据库中取
String username="liang";
String password="123321";
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
if (userToken.getUsername().equals(username)){
return null;//抛出异常 UnknownAccountException
}
//密码认证,shiro做
return new SimpleAuthenticationInfo("",password,"");
}
shiro整合mybatis
导入数据库驱动和mybatis各种依赖
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.1</version>
</dependency>
<!--引入mybatis,这是mybatis官方提供的适配springboot的,而不是springboot自己的-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
配置application.yml配置文件
spring:
datasource:
username: root
password: root
#加入时区报错了,就增加一个时区ok了?&serverTimezone=UTC&
url: jdbc:mysql://localhost:3306/mybatis?&serverTimezone=UTC&useUnicode=false&charactEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #配置数据源
#SpringBoot默认是不注入这些的,需要自己绑定
#druid数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity
#则导入log4j 依赖就行
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
application.properties
mybatis.type-aliases-package=com.liang.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
ok!连接数据库完成
写个text类测试一下,不然写代码太多起不来,找错范围太大
package com.liang;
import com.liang.service.UserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ShiroSpringbootApplicationTests {
@Autowired
private UserServiceImpl userService;
@Test
void contextLoads() {
System.out.println(userService.queryUserByName("sbsbsb"));
}
}
创建一个pojo User实体类
package com.liang.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;
}
mapper UserMapper接口
package com.liang.mapper;
import com.liang.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface UserMapper {
public User queryUserByName(String name);
}
在resources下建一个mapper UserMapper.xml写sql语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liang.mapper.UserMapper">
<select id="queryUserByName" parameterType="String" resultType="User">
select * from mybatis.user where name =#{name}
</select>
</mapper>
service UserService接口
package com.liang.service;
import com.liang.pojo.User;
public interface UserService {
public User queryUserByName(String name);
}
UserServiceImpl实现类
package com.liang.service;
import com.liang.mapper.UserMapper;
import com.liang.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
UserRealm
package com.liang.config;
import com.liang.pojo.User;
import com.liang.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
//自定义的UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=》授权doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=》认证doGetAuthenticationInfo");
//用户名,密码 数据库中取
// String username="liang";
// String password="123321";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//连接真实数据库
User user = userService.queryUserByName(userToken.getUsername());//会返回一个user对象 这里没有获取到值 记得看这里
if (user==null){//没有这个人,登录失败
return null;//UnknownAccountException
}
//密码认证,shiro做
return new SimpleAuthenticationInfo("",user.getPwd(),"");
}
}
shiro请求授权实现
ShiroConfig
package com.liang.config;
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 {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier( "securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证才能访问
user:必须拥有 记住我 功能才能用
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
//登录拦截
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/user/add","authc");
// filterMap.put("/user/update","authc");
//授权,正常的情况下,没有授权会跳到未授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
filterMap.put("/user/*", "authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录页面
bean.setLoginUrl("/toLogin");
//未授权页面
bean.setUnauthorizedUrl("/unauth");
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm 对象,需要自定义类 :第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
UserRealm
package com.liang.config;
import com.liang.pojo.User;
import com.liang.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;
//自定义的UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=》授权doGetAuthorizationInfo");
//SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
//拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到user对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
//return info
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=》认证doGetAuthenticationInfo");
//用户名,密码 数据库中取
// String username="liang";
// String password="123321";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//连接真实数据库
User user = userService.queryUserByName(userToken.getUsername());//会返回一个user对象 这里没有获取到值 记得看这里
if (user==null){//没有这个人,登录失败
return null;//UnknownAccountException
}
//密码认证,shiro做
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
MyController
@RequestMapping("/unauth")
@ResponseBody
public String unauthorized(){
return "未经授权,无法访问此页面 404!";
}
shiro整合thymeleaf
导入shiro-thymeleaf整合依赖
<!--shiro-thymeleaf整合-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
ShiroConfig
package com.liang.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 org.springframework.web.bind.annotation.RequestMapping;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier( "securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证才能访问
user:必须拥有 记住我 功能才能用
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
//登录拦截
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/user/add","authc");
// filterMap.put("/user/update","authc");
//授权,正常的情况下,没有授权会跳到未授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
filterMap.put("/user/*", "authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录页面
bean.setLoginUrl("/toLogin");
//未授权页面
bean.setUnauthorizedUrl("/unauth");
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm 对象,需要自定义类 :第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合shiroDialect: 用来整合shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
UserRealm
package com.liang.config;
import com.liang.pojo.User;
import com.liang.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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
//自定义的UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=》授权doGetAuthorizationInfo");
//SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
//拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到user对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
//return info
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=》认证doGetAuthenticationInfo");
//用户名,密码 数据库中取
// String username="liang";
// String password="123321";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//连接真实数据库
User user = userService.queryUserByName(userToken.getUsername());//会返回一个user对象 这里没有获取到值 记得看这里
if (user==null){//没有这个人,登录失败
return null;//UnknownAccountException
}
Subject currentSubject= SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser", user);
//密码认证,shiro做
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
MyController
package com.liang.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.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import sun.text.normalizer.NormalizerBase;
import java.util.Map;
@Controller
public class MyController {
@RequestMapping({"/","index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@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);//执行登录的方法,如果没有异常就说明ok了
return "index";
}catch (UnknownAccountException uae){//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException ice){//密码不存在
model.addAttribute("msg","密码错误");
return "login";
}
}
@RequestMapping("/unauth")
@ResponseBody
public String unauthorized(){
return "未经授权,无法访问此页面 404!";
}
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<!--从session中判断值!-->
<div th:if="${session.loginuser==null}">
<a th:href="@{/toLogin}">登录</a>
</div>
<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
开源模板分析
java自带的md5加密不安全,可以百度破解
账号:admin 密码:123456
导入别人项目首先要记得的几个点
- 数据库账号密码,记得改成自己的
- 注意端口
- layUI可以下载模板练手
- 模板之家,可以下载后台模板
Swagger
学习目标:
- 了解swagger的作用和概念
- 了解前后端分离
- 在springboot中集成swagger
swagger简介
前后端分离
vue+springboot
后端时代:前端只管理静态页面;html==>后端 模板引擎 jsp ==>后端是主力
前后端分离式时代:
-
后端:后端控制器,服务层,数据访问层
-
前端:前端控制层,视图层
- 伪造后端数据json。已经存在了,不需要后端,前端工程依旧能够跑起来
-
前后端如何交互?==》api
-
前后端相对独立,松耦合
-
前后端甚至可以部署在不同的服务器上
产生一个问题:
-
前后端集成联调,前端人员和后端人员无法做到“即使协商,今早解决问题”,最终导致问题爆发
解决方案:
- 首先指定schema【计划的提纲】,实时更新最新的api,降低集成的风险
- 早些年:指定world文档
- 前后端分离:
- 前端测试后端接口:postman
- 后端提供接口,需求实时更新最新的消息改动!
Swagger
- 号称世界上最流行的api框架
- RestFul api 文档在线自动生成工具=》api 文档与api定义同步更新
- 直接运行,可以在线测试api接口
- 支持多种语言:(java,php…)
官网:https://swagger.io/
在项目中使用swagger需要springfox;
- swagger2
- ui
springboot集成swagger
1.新建一个springboot=web项目
2.导入相关依赖
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
3.编写一个hello工程
4.配置swagge==>config
package com.liang.swagger.config;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig {
}
5.测试运行
配置swagger
swagger的bean实例Docket
写一个config SwaggerConfig配置类
package com.liang.swagger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 //开启swagger2
public class SwaggerConfig {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
//配置swagger信息 = apiinfo
private ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("梁伟浩", "http://blog.kuangstudy.com/", "1814356884@qq.com");
return new ApiInfo(
"梁伟浩的swagger api文档",
"现在开始永远不会太晚",
"v1.0",
"http://blog.kuangstudy.com/",
contact , "Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
配置扫描接口以及开关
- Docket.select()
配置扫描的接口
package com.liang.swagger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 //开启swagger2
public class SwaggerConfig {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// basePackage:指定要扫描的包
//any():扫描全部
//none():不扫描
//withClassAnnotation:扫描类上的注解
.apis(RequestHandlerSelectors.basePackage("com.liang.swagger.controller") )//配置这个后,只能访问这包下面的controller了
//paths():过滤什么路径
// .paths(PathSelectors.ant("/liang/**")) //全部过滤掉就访问扫描不到东西了
.build(); //build 工厂模式
}
//配置swagger信息 = apiinfo
private ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("梁伟浩", "http://blog.kuangstudy.com/", "1814356884@qq.com");
return new ApiInfo(
"梁伟浩的swagger api文档",
"现在开始永远不会太晚",
"v1.0",
"http://blog.kuangstudy.com/",
contact , "Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
配置是否启动swagger
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(false)//enable 是否启动swagger,如果为false,则swagger不能再服务器中访问
.select()//select和build是一套,不能在其中间做其他没有的属性方法
// basePackage:指定要扫描的包
//any():扫描全部
//none():不扫描
//withClassAnnotation:扫描类上的注解
.apis(RequestHandlerSelectors.basePackage("com.liang.swagger.controller") )//配置这个后,只能访问这包下面的controller了
//paths():过滤什么路径
// .paths(PathSelectors.ant("/liang/**"))
.build(); //build 工厂模式
}
我只希望我的swagger在生产环境中使用,在发布的时候不使用?
- 判断是不是生产环境 flag=false
- 注入enable(flag)
SwaggerConfig
package com.liang.swagger.config;
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 //开启swagger2
public class SwaggerConfig {
@Bean
public Docket docket(Environment environment){
//设置要显示的swagger环境
Profiles profiles = Profiles.of("dev","text");
//获取项目的环境
//通过environment.acceptsProfiles判断自己是否处在自己设定的环境当中
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(flag) //enable 是否启动swagger,如果为false,则swagger不能再服务器中访问
.select()//select和build是一套,不能在其中间做其他没有的属性方法
// basePackage:指定要扫描的包
//any():扫描全部
//none():不扫描
//withClassAnnotation:扫描类上的注解
.apis(RequestHandlerSelectors.basePackage("com.liang.swagger.controller") )//配置这个后,只能访问这包下面的controller了
//paths():过滤什么路径
// .paths(PathSelectors.ant("/liang/**"))
.build(); //build 工厂模式
}
//配置swagger信息 = apiinfo
private ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("梁伟浩", "http://blog.kuangstudy.com/", "1814356884@qq.com");
return new ApiInfo(
"梁伟浩的swagger api文档",
"现在开始永远不会太晚",
"v1.0",
"http://blog.kuangstudy.com/",
contact , "Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
然后模拟不同生产环境建立一个dev生产环境和pro上线运行环境配置文件,如果检验到生产环境就true可以访问,上线pro检验不到就是false无法访问,默认的环境是8080
application.properties
spring.profiles.active=pro
application-dev.properties
server.port=8080
application-pro.properties
server.port=8082
配置API文档的分组
1.groupName(“lwh”)
如何配置多个分组;
配置多个分组
@Bean
public Docket docket1(){
return new Docket(DocumentationType.SWAGGER_2).groupName("a");
}
@Bean
public Docket docket2(){
return new Docket(DocumentationType.SWAGGER_2).groupName("b");
}
@Bean
public Docket docket3(){
return new Docket(DocumentationType.SWAGGER_2).groupName("c");
}
实体类配置;
package com.liang.swagger.pojo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
//@Api(注释)
@ApiModel("用户实体类") //描述生成注释
public class User {
@ApiModelProperty("用户名")
public String username;
@ApiModelProperty("密码")
public String password;
}
controller
package com.liang.swagger.controller; import com.liang.swagger.pojo.User; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; 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 //没有页面返回一个字符串 public class HelloController { //error @GetMapping (value = "/hello") public String hello(){ return "hello"; } //只有我们接口中,返回值中存在实体类。,它就会被扫描到swagger中 @PostMapping(value = "/user") public User user(){ return new User(); } //Operation接口,不是放在类上的 @ApiOperation("hello控制类") @GetMapping(value = "/hello2") public String hello2(@ApiParam("用户名") String username){ return "hello"+username; } @ApiOperation("post控制类") @GetMapping(value = "/post") public User post(@ApiParam("用户名") User user){ int i =5/0; return user; } }
@ApiOperation("post控制类") @GetMapping(value = "/post") public User post(@ApiParam("用户名") User user){ return user; } 上面返回值是User你下面才能返回一个user对象
测试功能
swagger总结:
- 我们可以通过给swagger给一些难以理解的属性或者接口,增加注解信息
- 接口文档实时更新
- 可以在线测试
Swagger是一个优秀的工具,几乎所有大公司都有使用它
【注意点】 在正式发布的时候,关闭swagger!!!出于安全考虑,而且节省运行的内存
任务
异步任务
AsyncService
package com.liang.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.sql.SQLOutput;
@Service
public class AsyncService {
//告诉spring这是一个异步方法
@Async
public void hello() throws InterruptedException {
Thread.sleep(3000);
System.out.println("数据正在处理");
}
}
AsyncController
package com.liang.controller;
import com.liang.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncController {
@Autowired
AsyncService asyncService;
@RequestMapping("/hello")
public String hello() throws InterruptedException {
asyncService.hello();;//停止三秒
return "ok!!!";
}
}
Springboot09TextApplication
package com.liang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync//开启异步注解功能
@SpringBootApplication
public class Springboot09TextApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot09TextApplication.class, args);
}
}
开启异步任务后,客户端会首先获取到数据,而后台在运行sleep三秒后才出来的数据
邮件任务
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
编写配置application.properties文件
spring.mail.username=1814356884@qq.com
spring.mail.password=yjeapxevwxegehea
spring.mail.host=smtp.qq.com
#开启加密验证
spring.mail.properties.mail.smtp.enable=true
编写测试类
package com.liang;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
@SpringBootTest
class Springboot09TextApplicationTests {
@Autowired
JavaMailSenderImpl mailSender;
@Test
void contextLoads() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setSubject("早上好呀,梁伟浩");
mailMessage.setText("加油 努力 奋斗");
mailMessage.setTo("1814356884@qq.com");
mailMessage.setFrom("1814356884@qq.com");
mailSender.send(mailMessage);
}
@Test
void contextLoads2() throws MessagingException {
//一个复杂的邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
//组装
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
//正文
helper.setSubject("梁伟浩你好呀 plus版");
helper.setText("<p style='color:red'>加油 啊啊啊啊</p>",true);
//附件
helper.addAttachment("1.jpg",new File("C:\\Users\\Administrator\\Desktop\\1.jpg"));
helper.addAttachment("2.jpg",new File("C:\\Users\\Administrator\\Desktop\\1.jpg"));
helper.setTo("1814356884@qq.com");
helper.setFrom("1814356884@qq.com");
mailSender.send(mimeMessage);
}
}
邮件测试成功
/**加回车键
封装源码
/**
*
* @param html
* @param subject
* @param text
* @throws MessagingException
* @Author:liangwh
*/
public void sendMail(Boolean html,String subject,String text) throws MessagingException {
//一个复杂的邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
//组装
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);//是否支持多文本上传
//正文
helper.setSubject(subject);
helper.setText(text,true);
//附件
helper.addAttachment("1.jpg",new File("C:\\Users\\Administrator\\Desktop\\1.jpg"));
helper.addAttachment("2.jpg",new File("C:\\Users\\Administrator\\Desktop\\1.jpg"));
helper.setTo("1814356884@qq.com");
helper.setFrom("1814356884@qq.com");
mailSender.send(mimeMessage);
}
定时任务
-
TaskSchedluder 任务调度者
-
TaskExecutor 任务执行者
-
@EnableScheduling //开启定时功能的注解,在主启动程序加
-
@scheduled 什么时候执行
-
Cron 表达式
每天在这个时间点都会执行这个方法
package com.liang.service; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @Service public class ScheduledService { //在一个特定的时间执行这个方法 timer //cron 表达式 /* @Scheduled(cron = "0 * * * * 0-7") 秒 分 时 日 月 周几 每天 0代表秒 36代表分 8代表时 执行一次 30 0/5 10,18 **? 每天十点和18点,每隔五分钟执行一次 0 15 10?* 1-6 每个月的周一到周六,10.15分钟执行一次 @Scheduled(cron = "0/2 * * * * ?") 每两秒钟执行一次 */ @Scheduled(cron = "0 36 8 * * ?") public void hello(){ System.out.println("hello,你被执行了"); } }
分布式系统理论
分布式 Dubbo+zookeeper+springboot
什么是分布式系统?
Dubbo概念
什么是dubbo?
Apache Dubbo 是一款高性能、轻量级的开源Java RPC框架,他提供了三大核心能力:面向接口的远程调用,智能容错和负载均衡,以及服务自动注册和发现。
下载zookeeper的地址链接: http://archive.apache.org/dist/zookeeper/
Dubbo文档
本文介绍了网站应用的演进
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
适用于小型网站,小型管理系统,将所有功能都部署到一个功能里面,简单易用
缺点:
- 性能扩展比较难
- 协同开发问题
- 不利于升级维护
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职更易管理,性能扩展也更方便,更有针对性
缺点: 公共模块无法重复利用,开发性能的浪费
分布式服务架
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
RPC
什么是rpc?
RPC的两个核心模块:通讯,序列化
序列化:数据传输需要转化
Dubbo以及zookeeper安装
首先去下载一个zookeeper压缩包
Windows下载安装dubbo-admin
地址:https://github.com/apache/dubbo-admin/tree/master
在下载的目录下打包dubbo-admin:mvn clean package -Dmaven.text.skip=true
访问7001,账号密码都是root
服务注册发现实战
直接建一个空项目
再建一个模块 ,模块化开发
记得把端号改了 不然就端口占用了
通过百度查找maven,可以找到所以你要的依赖
导入依赖
<!--导入依赖 dubbo和zookeeper-->
<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-spring-boot-starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<!--zkclient-->
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<!--日志会冲突-->
<!-- 引入zookeeper -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<!--排除这个slf4j-log4j12-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
首先启动zookeeper服务器
再启动打包好的dubbo-admin包,然后访问监控localhost:7001
步骤:
前提:zookeeper服务已开始!
-
提供者提供服务
-
导入依赖
<!--导入依赖 dubbo和zookeeper--> <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-spring-boot-starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.3</version> </dependency> <!--zkclient--> <!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient --> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency> <!--日志会冲突--> <!-- 引入zookeeper --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.14</version> <!--排除这个slf4j-log4j12--> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency>
-
配置注册中心的地址,以及服务发现名和要扫描的表
TicketService
package com.liang.service; public interface TicketService { public String getTicket(); }
TicketServiceImpl
package com.liang.service; import org.apache.dubbo.config.annotation.Service; import org.springframework.stereotype.Component; //zookeeper:服务注册与发现 @Service //可以被扫描,在项目中启动就自动注册到注册中心 @Component //使用dubbo后尽量不要使用@service public class TicketServiceImpl implements TicketService { @Override public String getTicket() { return "《lwh真好》"; } }
application.properties
# 应用名称 spring.application.name=provider-server # 应用服务 WEB 访问端口 server.port=8081 # 服务应用名字 dubbo.application.name=provider-server # 注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 # 那些服务要被注册 dubbo.scan.base-packages=com.liang.
-
在想要注册的服务上面 加一个注解@service dubbo包下的
-
2. 消费者如何消费
-
导入依赖
-
配置注册中心的地址,配置自己的服务名
TicketService
package com.liang.service; public interface TicketService { public String getTicket(); }
UserService
package com.liang.service; import org.apache.dubbo.config.annotation.Reference; import org.springframework.stereotype.Service; @Service //放到容器中,这个就不是dubbo的包了 public class UserService { //想拿到provider生产的票,需要去注册中心拿到服务 @Reference //引用,pom坐标,可以定义路径相同的接口名 TicketService ticketService; public void buyTicket(){ String ticket = ticketService.getTicket(); System.out.println("在注册中心拿到的ticket==》"+ticket); } }
text测试类,看是否拿到注册中心的值
ConsumerServerApplicationTests
package com.liang; import com.liang.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class ConsumerServerApplicationTests { @Autowired UserService userService; @Test void contextLoads() { userService.buyTicket(); } }
-
从远程注入服务 @Reference
成功获取拿到注册中心的值
大展望和回顾
人 永远不要停下学习的脚步!
总结:
- 记得啊 反正程序哪个环节问题 第一时间检查这个环节 就比如我刚刚那个userToken.getUsername()取值的,但是我加了双引号“userToken.getUsername()”,导致取不到值,获取不到数据库的数据,登录不上去
- 有时候出问题报yml配置文件
错误,删掉yml重新建个复制黏贴可能可以喔
- service调dao层
- layUI可以下载模板练手
- 模板之家,可以下载后台模板
- 注意在创建module目录,不要重复文件夹路径