springboot整合security
参考:
1.什么是token
https://www.jianshu.com/p/24825a2683e6
2.SpringBoot安全管理–(一)SpringSecurity基本配置
https://www.cnblogs.com/crazy-lc/p/12361118.html
3.BCrypt 密码加密和解密
https://www.jianshu.com/p/fc910a1f7c8d/
4.springboot 的安全管理
https://blog.csdn.net/daqi1983/article/details/115508248
1.添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.添加接口
@RestController
public class HelloController {
// 3.进行身份认证
@GetMapping("/hello")
public String hello() {
return "hello codeL!";
}
}
3.启动项目会方向所有路径都被拦截下来了,需要登录才能进入
密码在idea的控制台上会显示出来,这是内部写好的,可以通过配置类去修改,下面有三种方法去修改。
Security包
需要修改Security配置就需要在Security包里面对一些类进行重写。
项目引入spring-boot-starter-security依赖启动器,MVC Security安全管理功能就会自动生效,其默认的安全配置是在SecurityAutoConfiguration和UserDetailsServiceAutoConfiguration中实现的。所以修改里面的配置可以得到我们自己想要的结果。
SecurityAutoConfiguration导入并自动化配置SpringBootWebSecurityConfiguration用于启动Web安全管理.
UserDetailsServiceAutoConfiguration用于配置用户身份信息.
可以通过自定义WebSecurityConfigurerAdapter类型的Bean组件来覆盖默认访问规则。
configure(HttpSecurity http):自定义用户授权管理 控制http的访问路径
configure(AuthenticationManagerBuilder auth) :身份验证管理器生成器 控制登陆身份
1.内存身份认证
/*
@EnableWebSecurity注解是一个组合注解,
主要包括@Configuration注解、
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class})注解
和@EnableGlobalAuthentication注解
*/
//网络安全配置适配器
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailServiceImpl userDetailService;
//自定义用户认证
@Override
// 身份验证管理器生成器 3.使用jdbc进行身份认证
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// B Crypt 密码编码器
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
// 1. 使用内存身份进行认证(测试)
// ===================================
auth.inMemoryAuthentication().passwordEncoder(encoder).withUser("codeLL").password(encoder.encode("123456")).roles("common").and()
.withUser("chen").password(encoder.encode("123")).roles("Vip");
}
2.基于jdbc身份认证
//2. 使用jdbc进行身份认证(开发)
String userSQL = "select username,password,valid from t_customer " + "where username = ?";
String authoritySQL = "select c.username,a.authority from t_customer c, " + "t_authority a,t_customer_authority ca where " + "ca.customer_id=c.id and ca.authority_id=a.id and c.username =?";
auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(dataSource).usersByUsernameQuery(userSQL).authoritiesByUsernameQuery(authoritySQL);
各个表之间的关系
测试之前,先把数据库配置信息,驱动包对应的依赖导进去
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
application.yaml
spring:
datasource:
username: root
password: abcde
url: jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
然后开启登录,根据数据库的信息进行登录。测试之前要把redis里面之前测试的数据缓存删除掉,不然会报序列化的错误。
3.UserDetailsService 身份认证
//3.userDetailService身份认证 这个可以直接使用service里面配置的
// userDetailService里面含有对象的详细信息
auth.userDetailsService(userDetailService).passwordEncoder(encoder);
逻辑图:
4.自定义用户授权管理
// 2.自定义用户授权管理 Http安全
@Override
protected void configure(HttpSecurity http) throws Exception {
// 路径“/"直接放行
http.authorizeRequests().antMatchers("/").permitAll()
// permitAll表示和登陆相关的接口不需要认证
// 放行“common 表示访问/common/**路径的必须要admin角色,后面两个也一样道理。
.antMatchers("/common/**").hasRole("common")
// 放行vip
.antMatchers("/vip/**").hasRole("vip")
// 登录的才能放行
.anyRequest().authenticated()//表示除了前面定义的url,后面的都得认证后访问(登陆后访问)
.and().formLogin();// permitAll表示和登陆相关的接口不需要认证
// 注销
http.logout().logoutUrl("/mylogout").logoutSuccessUrl("/");
// 经过测试,拦截成功
}
controller类
/**
* 文件控制器
*
* @author codel
* @date 2021/09/12
*/
@Controller
public class FileController {
/**
* 定义进入详细影片的api
*
* @param type 类型
* @param path 路径
* @return {@link String}
*/
@GetMapping("/{type}/{path}")
public String toDetail(@PathVariable("type") String type, @PathVariable("path") String path) {
return type + "/" + path;
}
}
配置视图:
会发现,没vip的权限不能进入vip的页面。
5.自定义用户退出
<form th:action="@{/mylogout}" method="post">
<label>
<input th:type="submit" th:value="注销" style="color: blue"/>
</label>
</form>
在SecurityConfig类,重写configure(HttpSecurity http)方法进行用户退出控制,在该方法中添加如下代码
// 2. 注销
http.logout().logoutUrl("/mylogout").logoutSuccessUrl("/");
6.获取登录用户信息
使用SecurityContextHolder获取用户信息
/**
* 通过Security提供的SecurityContextHolder获取登录用户信息
*/
@GetMapping("/getuserByContext")
@ResponseBody
public List<Object> getUser2() {
List<Object> list = new ArrayList<>();
// 获取应用上下文
SecurityContext context = SecurityContextHolder.getContext();
System.out.println("userDetails: " + context);
// 获取用户相关信息
Authentication authentication = context.getAuthentication();
UserDetails principal = (UserDetails) authentication.getPrincipal();
System.out.println(principal);
System.out.println("username: " + principal.getUsername());
list.add(context);
list.add(principal);
list.add(principal.getUsername());
return list;
}
用户信息:
7.记住我功能实现
记住时长:登录之后,注销之前或token失效之前
基于持久化Token方式
建立Token表
更新 configure(HttpSecurity http)配置:
// 3.配置记住我的功能
http.rememberMe().rememberMeParameter("rememberme").tokenValiditySeconds(200)
// 点击记住我,然后重启浏览器,发现不用输入密码和账号,直接进入首页
// 注意:这里是对cookie信息进行持久化管理,也可以使用简单加密的方式
.tokenRepository(tokenRepository());
// 经过测试,拦截成功
// 设置关闭CSRF功能
// http.csrf().disable();
}
/**
* 持久化Token存储
*/
@Bean
public JdbcTokenRepositoryImpl tokenRepository() {
// Jdbc 令牌存储库实现
JdbcTokenRepositoryImpl jr = new JdbcTokenRepositoryImpl();
jr.setDataSource(dataSource);
return jr;
}
了解token
重启项目进行效果测试,通过浏览器访问项目首页,输入正确的账户信息,
勾选记住我后,跳转到项目首页index.html,
数据库的变化
注销后,数据库数据没有了。
8.CSRF防护功能
也就是防止而已恶意增删改数据
html页面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>用户修改</title>
</head>
<body>
<div align="center">
<!-- 这种是thymeleaf模板方式配置属性请求,会默认携带CSRF Token的信息-->
<!-- <form method="post" th:action="@{/updateUser}">-->
<!-- 这是显性的配置CSRF token的方式进行获取用户验证信息-->
<form method="post" action="/updateUser">
<input type="hidden" th:name="${_csrf.getParameterName()}" th:value="${_csrf.token}">
<!-- 这是显示的方式,没有关闭默认的CSRF配置是不能进行增删改的-->
<!-- <form method="post" action="/updateUser">-->
用户名: <label>
<input type="text" name="username"/>
</label><br/>
密 码: <label>
<input type="password" name="password"/>
</label><br/>
<button type="submit">修改</button>
</form>
</div>
</body>
</html>
package com.codel.securitylogin.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* csrfcontroller
*
* @author codel
* @date 2021/09/12
*/
@Controller
public class CSRFController {
// 向用户修改页跳转
@GetMapping("/toUpdate")
public String toUpdate() {
return "csrf/csrf";
}
// 用户修改提交处理
@ResponseBody
@PostMapping(value = "/updateUser")
public String updateUser(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
System.out.println(username);
System.out.println(password);
String csrf_token = request.getParameter("_csrf");
System.out.println(csrf_token);
return "ok";
}
}
测试,没有关闭csrf会显示报错,因为security默认开启csrf
// 设置关闭CSRF功能
http.csrf().disable();
关闭后就可以修改成功了。
但是这样不安全,所以要使用CSRF Token方式来配置
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>用户修改</title>
</head>
<body>
<div align="center">
<!-- 这种是thymeleaf模板方式配置属性请求,会默认携带CSRF Token的信息-->
<!-- <form method="post" th:action="@{/updateUser}">-->
<!-- 这是显性的配置CSRF token的方式进行获取用户验证信息-->
<form method="post" action="/updateUser">
<input type="hidden" th:name="${_csrf.getParameterName()}" th:value="${_csrf.token}">
<!-- 这是显示的方式,没有关闭默认的CSRF配置是不能进行增删改的-->
<!-- <form method="post" action="/updateUser">-->
用户名: <label>
<input type="text" name="username"/>
</label><br/>
密 码: <label>
<input type="password" name="password"/>
</label><br/>
<button type="submit">修改</button>
</form>
</div>
</body>
</html>
使用Thymeleaf模板会默认带着token的,所以可以提交数据修改。
9.前端页面管理
1.添加thymeleaf-extras-springsecurity5依赖启动器
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
2.修改前端页面,使用Security相关标签进行页面控制
在index.html页面中引入Security安全标签
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
修改后的index页面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" lang="en">
<!--页面顶部通过“xmlns:sec”引入了Security安全标签,页面中根据需要编写了4个-->
<!--<div>模块.-->
<body>
<div sec:authorize="isAnonymous()">
<h2 align="center">游客您好,如果想查看电影<a th:href="@{/userLogin}">请登录</a></h2></div>
<div sec:authorize="isAuthenticated()">
<h2 align="center"><span sec:authentication="name" style="color: #007bff"></span>
您好,您的用户权限为<span sec:authentication="principal.authorities" style="color:darkkhaki"></span>,您有权观看以下电影</h2>
<form th:action="@{/mylogout}" method="post">
<label>
<input th:type="submit" th:value="注销" style="color: blue"/>
</label>
</form>
</div>
<div sec:authorize="hasRole('common')">
<h3>普通电影</h3>
<ul>
<li><a th:href="@{/common/1}">我不是药神</a></li>
<li><a th:href="@{/common/2}">夏洛特烦恼</a></li>
</ul>
</div>
<div sec:authorize="hasAuthority('ROLE_vip')">
<h3>VIP专享</h3>
<ul>
<li><a th:href="@{/vip/1}">速度与激情</a></li>
<li><a th:href="@{/vip/2}">猩球崛起</a></li>
</ul>
</div>
</body>
</html>