这里写目录标题
springboot-02-config
yml和properties两种配置文件的不同,yaml中-些属性的书写格式,包括实体类、数组、map、 set、 引入外部类
实体类要通过@Component
将该类注入到spring容器中,
若将配置文件中的值赋值给容器中的变量,若为yml格式 要通过@ConfigurationProperties(prefix=" person")
格式引入
application.yaml
person:
name: 小明
age: 12
hobbies: [sing,dance,show]
sports: [bas,足球,ping]
map: {1: 小王,2: 小刘}
dog:
name: 金毛
age: 5
date: 2020/08/07
person.class外部引入application.yaml配置文件
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
//prefix = "person"和ymal声明的实体类person进行绑定起来
@ConfigurationProperties(prefix = "person")
@Validated//数据校验
/*@PropertySource(value = "classpath:application.properties")*/
public class Person {
/*@Email*/
/*@Value("张哲${random.uuid}")*/
private String name;
/*@Value("${random.int}")*/
private Integer age;
/*@Value("#{9+2}")*/
private Integer heigh;
private String[] hobbies;
private List<String> sports;
private Map map;
private Dog dog;
private Date date;
}
若properties文件要引入@PropertySources(value= "classpath:application.properties")
并且通过@Value(" ")
变量赋值,其中可以引入数据校验@Validated
ym|配置文件的多环境切换,通过"--- "
就可以实现在一个yml文件中实现多环境切换, 其中spring:profiles:dev
为标识该环境,通过spring:profiles:active:dev
激活该环境
深度分析了自动配置源码的实现原理HttpEncodingAutoConfig
springboot-03-web
主要讲了静态页面的访问,引入静态资源有两种方式
(1)通过导入webjars
依赖, 访问该依赖下webjars
下的静态资源;
<!--引入webjars-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
(2)还有就是把静态资源放到resources、static、 public、
(优先级逐渐降低)下面也能访问到静态页面,但一般把静态样式css、js、 img
都放到static下面;一般欢迎页放在public
下面,以index.html
的形式命名;
还有templates包下放主要的html页面,通过控制器、跳转到该下的页面
引入模板引擎thymeleaf
依赖还有一些用法 th:text、th:each、 th:if
等
<!--引入webjars-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
扩展mvc @Configuration
配置类的详细讲解包括自定义解析器实现ViewResolver
接口、实现自定义视图跳转重写addViewControllers
方法、格式化Format
等 最后详细讲解了@EnableWebMvc
注解
springboot-03-crud(实现了增删改查)
实现了数据的增删改查操作,其中包含了很多知识点比如restful风格的使用
国际化(实现LocaleResolver接口并将该类@Bean到MyMvcConfig类中)
登录的截操作(实现HandlerInterceptor并在MyMvcConfig类中重写addInterceptors方法并注入自定义的拦截类)这些都需要在MyMvbcConfig配置类中
国际化
1.在resources包下面,创建i18n
包( internationalization)缩写
2.创建login.properties配置文件和login_zh_CN.properties配置文件,自动帮我们整合在了一个包下面
3.接下来我们右键Resource Bundle 'login’创建login_en_US.properties
4.接下来我们在配置文件中添加内容 这里的login.xxx只是我们起的别名,方便前台根据#{login.xxx}取到国际化的值
配置完成
代码实现国际化的转换
来看下MessageSourceAutoConfiguration这个类
打开MessageSourceProperties这个类
application.properties 找到了国际化的配置文件
前台页面取国际化的值,我们根据thymeleaf模板引擎语法,用#{}来拿国际化的值
这样很难操控中英文变换,接下来我们通过按钮实现切换语言
这里th:href="@{/(l='zh_CN')}"
就请求的路径就为/?l=zh_CN
首先我们看下源码,打开webMvcConfiguration找到localeResolver这个类
总之要实现国际化,我们需要自定义一个组件LocalResolver
把自定义的组件配置到spring容器中去 @Bean
public class MyLocaleResolver implements LocaleResolver {
//实现国际化
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获得前台的请求参值的链接
String language = request.getParameter("l");
//System.out.println("----->"+language);
//如果没有就使用默认的
Locale locale = Locale.getDefault();
//如果请求的参数不为空,且携带了国际化的参数zh_CN en_US 就分隔出来
if (!StringUtils.isEmpty(language)){
//分隔字符串
String[] split = language.split("_");
// zh_CN 国家 地区
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
把自定义的组件配置到spring容器中去
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//注册国际化组件 放到spring容器中 才生效
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
}
实现登录,
@RequestMapping("/user/login")
public String login(@RequestParam("username") String name,
@RequestParam("password") String pwd,
Model model, HttpSession session) {
if (!StringUtils.isEmpty(name) && "123".equals(pwd)) {
//登录成功 跳转首页
session.setAttribute("loginUser", name);
return "redirect:/main.html";
} else {
//登陆失败 跳转到 登录页面 并携带错误信息
model.addAttribute("msg", "用户名不存或密码错误");
return "index";
}
}
MyMvcConfig.class
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 一般跳转到 首页用自定义跳转请求
//请求跳转到 主页面 和控制器作用相同
registry.addViewController("/").setViewName("index");
//urlPath:是控制器返回的伪路径,setViewName跳转的页面,dashboard.html
/*
这样进行跳转的好处 1.浏览器不在出现真实的访问路径,安全性提高
2. 若大量的页面跳转到该页面,可读性高
*/
registry.addViewController("/main.html").setViewName("dashboard");
}
自定义public void addViewControllers(ViewControllerRegistry registry) 方法,就是中转了一下控制器的请求路径,使浏览器不出现真实的请求路径,那么这样就实现了登录的操作,肯定是不安全的,否则不登录就能进入首页,接下来实现拦截器操作。
拦截操作
自定义HandlerInterceptor的实现类,这是springmvc的一个拦截操作
public class loginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录成功后获得用户的session
Object loginUser = request.getSession().getAttribute("loginUser");
//System.out.println("user=====>"+loginUser);
if (loginUser == null) {
//拦截
request.setAttribute("msg", "没有权限访问.请先登录");
//转发到 登录页面
request.getRequestDispatcher("/").forward(request, response);
return false;
} else {
return true;
}
}
}
重写WebMvcConfigurer 拦截器的实现方法addInterceptors并将自定义的拦截器@Bean到spring容器中
注册 自定义类
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 一般跳转到 首页用自定义跳转请求
//请求跳转到 主页面 和控制器作用相同
registry.addViewController("/").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
//注册国际化组件 放到spring容器中 才生效
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
/*//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new loginHandlerInterceptor()).addPathPatterns("/**")
// 拦截...请求 并且放行...请求
.excludePathPatterns("/", "/index.html", "/user/login", "/css/**", "js/**", "img/**");
}*/
@Bean
public HandlerInterceptor handlerInterceptor() {
return new loginHandlerInterceptor();
}
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor()).addPathPatterns("/**")
// 拦截...请求 并且放行...请求
.excludePathPatterns("/", "/index.html", "/user/login", "/css/**", "js/**", "img/**");
}
}
注册组件;前端抽取公共页面(详情见thymeleaf文档8.0
)
要抽取的片段
<div th:fragment="city"></div>
或者
<div id="city"><</div>
要引入的片段
//三种形式:th:insert、th:replace、th:include 其中th:replace使用频率更广
<div th:replace="commo/bar::city"></div>
或者
<div th:replace="commo/bar::#city"></div>
修改数据时运用resultful风格的put提交表单时,要实现这段代码
html<input type= "hidden" name=" method" value=" put" th:if="${emp!=null}">`
springboot-04-jdbc(整合jdbc和引入druid数据源)
整合了jdbc 在yml文件中注册了jdbc的四大驱动,还有将数据源更改成为了druid数据源,并添加了后台监控器 和web过滤器
注意要引入druid
、log4j
依赖
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
在.yml文件中引入jdbc四大驱动 及druid数据源专有配置
spring:
datasource:
username: root
password: '061298'
url: jdbc:mysql://localhost:3306/mybaits?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
##更换为druid 数据源
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#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.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
后台监控和wb过滤器
@Configuration
public class DruidMyConfig {
/*
将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
@ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控 相当于web.xml
// 因为springboot内置了 servlet容器 所以没有web.xml ServletRegistrationBean 替代了xml
@Bean
public ServletRegistrationBean servletRegistrationBean(){
ServletRegistrationBean<StatViewServlet> bean=new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
//后台需要有人登陆 账号密码
Map<String, String> initParameters = new HashMap<>();
//增加配置
//登陆key 是固定的 不能变
initParameters.put("loginUsername","admin");
initParameters.put("loginPassword","123456");
//允许谁访问 空代表所有人
initParameters.put("allow","");
//设置初始化 参数
bean.setInitParameters(initParameters);
return bean;
}
//过滤器
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new WebStatFilter());
//过滤哪些请求
Map<String,String> initParameters = new HashMap<>();
//这些请求 不需要过滤
initParameters.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
}
Controller处理业务 没有整合mybatis
@RestController
public class JdbcController {
@Autowired
private JdbcTemplate jdbcTemplate;
@RequestMapping("/select")
public String select() {
String sql = "select * from mybaits.blog";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
return maps.toString();
}
@RequestMapping("/insert")
public String add() {
String sql = "insert into mybaits.blog(id,title,author,create_time) values(6,'你好','张哲','2020-05-06')";
jdbcTemplate.update(sql);
return "添加成功";
}
@RequestMapping("/del/{id}")
public String del(@PathVariable("id") Integer id) {
String sql = "delete from mybaits.blog where id=?";
jdbcTemplate.update(sql,id);
return "删除成功";
}
@RequestMapping("/update/{id}")
public String update(@PathVariable("id") Integer id) {
String sql = "update mybaits.blog set title='王道',author='小王' where id="+id;
jdbcTemplate.update(sql);
return "修改成功";
}
}
springboot-05-mybatis(整合mybatis)
引入mybatis-springboot
依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis自己的一个依赖 不属于springboot-->
<!--`mybatis-springboot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
这里不需要引入spring-boot-starter-jdbc
依赖,因为引入的mysql-connector-java
依赖已经含有jdbc配置
application.yml
spring:
datasource:
username: root
password: '061298'
url: jdbc:mysql://localhost:3306/mybaits?serverTimezone=UTC&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,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
type-aliases-package: com.zz.pojo
mapper-locations: classpath:mapper/*.xml
#引入mybatis核心文件
#config-location:
创建各种包
UserMapper.java(注意要在Mapper接口类中添加@Mapper注解)
//表示本类是一个 Mapper接口类 相当于注册到了spring容器中
@Mapper
@Repository
public interface UserMapper {
List<User> getAllUser();
User getUserById(@PathVariable("id") Integer id);
int addUser(User user);
int delUserById(@PathVariable("id") Integer id);
int updateUser(User user);
}
或者 在主程序了中添加```@MapperScan(“com.zz.mapper”)``注解
@SpringBootApplication
//相当于@Mapper注解
//@MapperScan("com.zz.mapper")
public class Springboot05MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot05MybatisApplication.class, args);
}
}
UserMapper.xml
文件 一般 写在resources
下 也可以和UserMapper.java
在同一包下面
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.zz.mapper.UserMapper">
<select id="getAllUser" resultType="User">
select * from mybaits.user;
</select>
<select id="getUserById" resultType="User">
select * from mybaits.user where id=#{id};
</select>
<insert id="addUser" parameterType="User">
insert into mybaits.user(id, name, pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="delUserById" >
delete from mybaits.user where id=#{id}
</delete>
<update id="updateUser" parameterType="User">
update mybaits.user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
</mapper>
注册结果集mapper映射 要在.yml
文件中
mybatis:
type-aliases-package: com.zz.pojo
mapper-locations: classpath:mapper/*.xml
#classpath:表示resources这一目录下的文件
#引入mybatis核心文件
#config-location:classpath:mybatis.xml
或者定义一个mybatis.xml配置文件 通过config-location:
在.yml
中引入
注意classpath:后面不需要添加空格 直接写resource下的文件名
注意:当数据库的字段值以下划线,实体类中的属性以驼峰形式命名时,这时候就访问不到该字段数据库中的值了
实体类中属性值:private String userName
数据库中字段值:user_name
为了能正常取到数据库中该字段的值,要解决这一问题其中之一就是在配置文件做手动配置 接下来我们先来看下源码
首先我们先来打开mybatis自动配置类MybatisAutoConfiguration
找到ConfigurationCustomizer
点进去
之后会看到该类是一个接口,点进去传的参数
里面会有很多的属性变量
最后在DruidMyConfig
中进行配置绑定
//实体类属性与数据库字段值做驼峰转换
@Bean
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer() {
//重写该接口里的customize方法
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
那么最后输出结果就正常取到了该字段下的值
当然还可以在.yml配置文件中直接开启
看下源码 找到MybatisConfig
可以看到有很多的配置属性
点开Configuration
里面有很多属性,其中驼峰转换也在里面
application.yml
springboot-05-crud
在springboot-03-crud
的基础上 实现了mybatis连接数据库的操作,引入了Employee、Department
两张表,进行数据的crud操作。
需要重要掌握的就是,在多对一的情况下 ,对应“一”表的主键在“多”表中做外键,怎么才能让这两个属性关系进行联系,通过在Employee.xml
文件中注入sql语句来实现
Employee.xml
<!--结果嵌套处理-->
<select id="getAll" resultMap="EmployeeDep">
select e.id eid,e.lastName elastName,e.email eemail,e.gender egender,e.birth ebirth,d.departmentName ddepartmentName
from springboot_employee.employee e,springboot_employee.department d where e.did=d.id;
</select>
<resultMap id="EmployeeDep" type="Employee">
<result property="id" column="eid"/>
<result property="lastName" column="elastName"/>
<result property="email" column="eemail"/>
<result property="gender" column="egender"/>
<result property="birth" column="ebirth"/>
<association property="department" javaType="Department">
<result property="departmentName" column="ddepartmentName"/>
</association>
</resultMap>
springboot_employee
数据库
employee
CREATE TABLE employee(
`id` int(10) NOT NULL auto_increment,
name VARCHAR(14) DEFAULT NULL,
password VALUES(14) DEFAULT NULL,
email VARCHAR(25) DEFAULT NULL,
gender INT(10) DEFAULT NULL,
birth datetime(5) DEFAULT NULL,
did int (10),
PRIMARY KEY (id)
) ENGINE = InnoDB CHARACTER SET = utf8
department
CREATE TABLE department(
`id` int(10) NOT NULL auto_increment,
departmentName VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (id)
)ENGINE = InnoDB CHARACTER SET = utf8
实现WebMvcConfigurer接口
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 一般跳转到 首页用自定义跳转请求
//请求跳转到 主页面 和控制器作用相同
registry.addViewController("/").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
//注册国际化组件 放到spring容器中 才生效
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new loginHandlerInterceptor()).addPathPatterns("/**")
// 拦截...请求 并且放行...请求
.excludePathPatterns("/", "/user/login", "/css/**", "js/**", "img/**");
}
//配置视图解析器
@Bean
public InternalResourceViewResolver resourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
//请求视图文件的前缀地址
internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
//请求视图文件的后缀
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
//注册视图解析器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(resourceViewResolver());
}
}
具体 见代码
springboot-06-security
1、导入依赖
<!--thymeleaf-springsecurity5整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<!--security springboot整合包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
导入前端页面 如下
Controller
@Controller
public class LoginController {
@RequestMapping("/toLogin")
public String login() {
return "views/login";
}
@RequestMapping("/index")
public String index() {
return "index";
}
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") Integer id) {
return "views/level1/" + id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id") Integer id) {
return "views/level2/" + id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id") Integer id) {
return "views/level3/" + id;
}
}
security 配置文件SecurityConfig
首先继承自WebSecurityConfigurerAdapter
类 并
其次实现rotected void configure(HttpSecurity http)
授权 和protected void configure(AuthenticationManagerBuilder auth)
权限认证策略方法
注意为了保证密码的安全,我们要实现密码加密,其中密码加密的方法很多我们选择BCryptPasswordEncoder()
方法操作(ctrl+N 搜索passwordEncoder
)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//全部用户访问
.mvcMatchers("/").permitAll()
//... 用户访问... 页面
.mvcMatchers("/level1/**").hasRole("vip1")
.mvcMatchers("/level2/**").hasRole("vip2")
.mvcMatchers("/level3/**").hasRole("vip3");
/* 当没有权限时 ,会默认到系统的登录页面
定制登录页 loginPage("/toLogin")
默认登录的用户名 密码参数分别为 username password 要和表单的name通过设置保持一致
*/
http.formLogin().loginPage("/toLogin").usernameParameter("username").passwordParameter("password").loginProcessingUrl("/login");
//关闭csrf 功能 注销
http.csrf().disable();
//注销 跳转路径
http.logout().logoutSuccessUrl("/index");
//记住我 原理保存cookie 两周
//默认参数 remember-me
http.rememberMe().rememberMeParameter("remember-me");
}
//进行权限认证策略
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//正常情况下 这些数据应该从数据库中获取
//密码编码 PasswordEncoder 要对密码进行加密
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("zz").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3");
}
/*@Bean
BCryptPasswordEncoder passwordAuthentication(){
return new BCryptPasswordEncoder();
}*/
}
模仿用户权限注销登录
1、添加seciurity-spring
依赖
<!--thymeleaf-spring整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
2、添加头文件
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
3、更改首页index.html
代码
使其达到到当用户没有登录的时候,只出现用户登录的提示,当用户登录完成后,首页只出现用户名和注销的提示;还有当指定用户登录时,只出现该用户有访问权限的页面
<div class="ui secondary menu">
<a class="item" th:href="@{/index}">首页</a>
<!--登录-->
<div class="right menu">
<!--如果用户 没登录就显示-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/login}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--当用户登录后 显示用户名 和注销提示-->
<div sec:authorize="isAuthenticated()">
<a class="item">
用户名: <span sec:authentication="name"></span>
</a>
</div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i> 注销
</a>
</div>
</div>
</div>
<!--根据不同用户 显示权限页面-->
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1</h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2</h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 3</h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
</div>
</div>
</div>
</div>
开启记住我功能实现
用户自定义登录页面,请求路径为"/toLogin"
当用户点击自己设置的登录按钮时,会请求到自定义的登录页面,怎么才能让自定义的登录页面和系统的登录的页面请求相同的参数,通过在http.formLogin()
后面添加相应的方法来实现
//当没有权限时 ,会默认到系统的登录页面
//定制登录页 loginPage("/toLogin")
// 默认登录的用户名 密码参数分别为 username password 要和表单的name通过设置保持一致
http.formLogin().loginPage("/toLogin").usernameParameter("username").passwordParameter("password").loginProcessingUrl("/login");
注意自定义表单的action
为
<form th:action="@{/login}" method="post">
并添加记住我
<input type="checkbox" name="remember-me">记住我
以上就是简单的spring-security
权限登录功能的实现
springboot-07-hello shiro(maven)
-
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。【百度百科】
-
ApachevShiro注重易用性,因此您可以依靠安全,稳定的身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序。
-
Apache Shiro是一个功能强大且易于使用的Java安全框架,它为开发人员提供了一种直观,全面的身份验证,授权,加密和会话管理解决方案。
-
实际上,它可以管理应用程序安全性的所有方面,同时尽可能避免干扰。它建立在可靠的界面驱动设计和OO原则的基础上,可在您可以想象的任何地方实现自定义行为。但是,只要对所有内容都使用合理的默认值,就可以像应用程序安全性一样“轻松”。至少那是我们所追求的。
shiro十分钟快速入门
1、创建一个maven项目
2、导入依赖
<dependencies>
<!--shiro核心类-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<!--log4j 日志-->
<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>
3、创建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
4、创建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
5、创建Quickstart.java
/**
* @Author 张哲
* @Date 2020/8/20 19:02
*/
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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) {
//通过工厂模式 将shiro.ini文件加载进来
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 获取当前的用户对象
Subject currentUser = SecurityUtils.getSubject();
// 通过当前用户拿到session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Session---->someKey:" + value);
}
//判断当前用户是否被认证
if (!currentUser.isAuthenticated()) {
//UsernamePasswordToken是一个简单的包含username及password即用户名及密码的登录验证用token
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//记住我
token.setRememberMe(true);
try {
//登录当前用户 如果登录失败就抛出异常
currentUser.login(token);
log.info("用户登录成功");
} catch (UnknownAccountException uae) {
log.info("不存在该用户的名字------>[" + token.getPrincipal() + "]");
} catch (IncorrectCredentialsException ice) {
log.info("该用户的密码登录错误------>[" + token.getPrincipal() + "]");
} catch (LockedAccountException lae) {
log.info("该用户名被锁定");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
log.info("认证错误");
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] 登录成功");
log.info("该用户地址--->"+currentUser);
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("该用户拥有该角色");
} else {
log.info("对不起,该用户没有该角色");
}
//粗粒度
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("该用户拥有该权限");
} else {
log.info("sorry,该用户没有改权限");
}
//细粒度
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//注销
currentUser.logout();
//退出
System.exit(0);
}
}
springboot-08-shiro(整合springboot)
1、导入spring-shiro
依赖
.<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--thymeleaf-shiro整合包-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!--spring-shiro依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--springboot-mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、ShiroConfig
配置类
/*
subject 用户
securityManager 管理所有用户
Realm 连接数据
*/
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("security") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//安全管理器
bean.setSecurityManager(securityManager);
/*
anon:无需认证就可以访问
authc:必须认证了才能访问
perms: 拥有某个资源的权限才能使用
user: 拥该用户对象才能使用 记住我
roles: 拥有某个角色权限 才能使用
*/
Map<String, String> filterMap = new LinkedHashMap<>();
//用户 授权
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/select","perms[user:select]");
//登录拦截
filterMap.put("/user/*", "authc");
bean.setFilterChainDefinitionMap(filterMap);
//用户没有权限 去登录页面
//bean.setLoginUrl("/toLogin");
//角色没有权限 去未授权页面
bean.setUnauthorizedUrl("/noautoh");
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "security")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//关联userRealm
manager.setRealm(userRealm);
return manager;
}
//Realm 需要自定义
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
//ShiroDialect 用来整合shiro-thymeleaf
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
3、自定义UserRealm
类 进行认证和授权操作
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录对象的地址
Subject subject = SecurityUtils.getSubject();
//拿到当前登录的user对象
User currectUser = (User)subject.getPrincipal();
System.out.println(currectUser);
//设置当前用户的 权限 实际上从数据库中获取用户的权限
info.addStringPermission(currectUser.getPrerm());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证");
//定义 用户名 密码
/*String name = "root";
String pwd = "123";*/
//获取控制台 封装到UsernamePasswordToken中的用户名 和密码
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
User user = userService.getUserByName(userToken.getUsername());
//判断用户输入的用户名、密码是否和数据库中的相等
if (user==null){
//没有这个人 抛出UnknownAccountException异常
return null;
}
//把登录的对象放到session中
Subject currectUser = SecurityUtils.getSubject();
Session session = currectUser.getSession();
session.setAttribute("loginUser",user);
//加密 MD5
//密码 验证 shiro来做
//参数:用户名对象 密码 认证对象
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
4、ShiroController
@Controller
public class ShiroController {
@RequestMapping({"/", "/index"})
public String index() {
return "index";
}
@RequestMapping("/user/add")
public String add() {
return "/user/add";
}
@RequestMapping("/user/select")
public String select() {
return "/user/select";
}
@RequestMapping("/toLogin")
public String login() {
return "login";
}
@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) {
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//实际用来 获取前台的参数
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//登录 如果没有抛出异常 说明登录成功
//异常快捷键ctrl+alt+t
try {
//登录成功 返回首页
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
return "login";
}
}
@RequestMapping("/noautoh")
@ResponseBody
public String noauth() {
return "未授权 无法访问此页面";
}
@RequestMapping("/logout")
public String logout() {
//注销 移除session 后跳到登录页面
Subject currectSubject = SecurityUtils.getSubject();
Session session = currectSubject.getSession();
session.removeAttribute("loginUser");
return "login";
}
}
application.yml
spring:
datasource:
username: root
password: '061298'
url: jdbc:mysql://localhost:3306/mybaits?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
##更换为druid 数据源
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#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.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
type-aliases-package: com.zz.pojo
mapper-locations: classpath:mapper/*.xml
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>
<div th:if="${session.loginUser}==null">
<a th:href="@{/toLogin}">登录</a>
</div>
<div th:if="${session.loginUser}!=null">
<a th:href="@{/logout}">注销</a>
</div>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">添加</a>
</div>
<div shiro:hasPermission="user:select">
<a th:href="@{/user/select}">查找</a>
</div>
</body>
</html>
结构图