SpringSecurity(力争好理解,最细)
目前比较出名的安全框架有shiro和security,security出身名门,spring框架给予了很好的支持。
- security的操作颗粒度更高,安全性也会相对更好些。
- spring框架中security的使用相对于shiro更加便捷。
security中文文档,有兴趣可以看看官方文档
https://www.springcloud.cc/spring-security-zhcn.html
1,什么是安全管理
整个项目的开发过程中,后端程序员最核心的业务,就是给前端提供一系列的api接口。
- 前端需要一张静态资源图片,给她一个图片URL地址
- 前端需要播放一个视频,给她一个视频资源URL
- 前端需要新建一个用户,给她一个URL
- 不管前端需要什么,我们给到的都是一个冷冰冰的URl地址。
所以我们能不能这么理解,互联网项目的 访问安全,就是对基于对URL地址的安全管理。
2,怎样实现安全管理
所谓的安全管理,通俗的讲,就是把资源给到我想给的人
怎样才能把资源给到我想给的人呢,我们需要构建一个安全模型。我们把我们拥有的视频,图片,以及可以提供的服务称之为资源,把需要来获取资源的对象称之为用户。
2.1资源对应关系表
我们在用所有的用户和资源之间建立一 一个对应关系表,在表中规定,A资源能被a,b,c用户访问,B资源能被a,e,g用户访问,有了这样的对应关系表,资源的访问安全也就实现了。
2.2资源权限
如果我们的用户众多,资源众多,那么对应关系表的建立会十分的复杂,这张表也会非常的庞大。为了简化这样表,我们提出做一个新的概念:资源权限——访问资源需要的权利
假设A,B 资源我们想给的用户都新注册的用户,我们创建一个”newUser“的权限,把A,B两个资源划分到”newUser“的权限下,给新注册的用户贴上”newUser“的权限。那么我们的资源对应关系表可以极大的简化。
2.3用户角色
我们的用户需要是多样的,一个用户需要访问很多的资源,那么我们就需要给这个用户一 一添加需要的权限。如果我们的用户太多,给每个用户都添加多个权限,这也是比较麻烦的事,也会给我们在数据库中存储用户信息增加一点麻烦。我们发现用户与用户之间是有很多相似处的,可以按相似度进行分群,比如管理员用户,新用户,老用户等等。我们把这个相似处,抽象出来便有了角色。
3 security的安全管理流程
-
给资源添加访问限制权限
- 资源是以URL做为访路径的
- 给资源添加限制权限,也就是给路径添加限制规则
- 资源的限制权限,可以值指定权限,也可以是不要权限(全体用户)
-
获取用户信息
- 用户在提交登录后,security从数据库或者内存中获取用户信息,判断用户信息是否有一致
- 用户信息一致,用户状态转为登录
- 获取用户权限或角色信息,给用户添加对象权限
-
判断当前用户是否拥有权限
- 用户在请求需要权限的资源时,需要判断用户是否有对应权限。
- 如果当前用户未登录,跳转到登录,获取用户信息
4 项目实战
1.创建项目
- 创建一个springboo项目
- thymeleaf做页面模板引擎
- MySql做数据库
- mybatis做数据库管理
- 通过spring initial构建项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CfEvQGQN-1647606217093)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318170444133.png)]
2.在pom.xml文件中添加mybatis依赖
<!-- springboot 没给Mybatis做启动器 添加mybatis的启动器依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--mysql连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
3,添加资源文件,这里以HTML做资源文件,在resources/templates文件下添加
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-isFT4Vij-1647606217095)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318171200802.png)]
4,在数据库中新建user表,并插入数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lf3nPenr-1647606217095)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318171853076.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a0gj9i7U-1647606217096)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318171914082.png)]
5.创建UesrInfo类
package com.jade.entity;
import lombok.Data;
@Data
public class UserInfo {
public Long id;
public String username;
public String password;
public String role;
public String power;
}
6,创建UserInfoRepository接口类,做mybatis的代理调用对象
package com.jade.Repository;
import com.jade.entity.UserInfo;
import org.springframework.stereotype.Repository;
@Repository
public interface UserInfoRepository {
public UserInfo findByUsername(String username);
}
7,新建UserInfomapper.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.jade.Repository.UserInfoRepository">
<select id="findByUsername" parameterType="java.lang.String" resultType="com.jade.entity.UserInfo">
select * from user where username=#{value}
</select>
</mapper>
8.在启动类上添加导入mapper注解
@SpringBootApplication
@MapperScan("com.jade.Repository")
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
}
9.写application配置文件,我喜欢用yml格式,把application.properties修改application.yml
server:
port: 8080
spring:
# 配置视图模板配置器
thymeleaf:
mode: HTML
prefix: classpath:/templates/
suffix: .html
#数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123123
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:/mappers/*.xml
10配置页面访问路径,新建MvcConfig类实现WebMvcConfigurer
package com.jade.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//配置页面访问路径,或者也可以写controller
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/login_on").setViewName("login");
registry.addViewController("/main").setViewName("main");
registry.addViewController("/user").setViewName("user");
}
//如果有静态资源
/* @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}*/
}
这时候启动项目,访问项目是不能访问的,security导入包后,就自动配置了一个用户,这是访问会出现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xpVyaIeo-1647606217097)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318181847973.png)]
开始配置security
11.创建MyUserDetails类 实现UserDetailsService,来完成用户认证
package com.jade.config;
import com.jade.Repository.UserInfoRepository;
import com.jade.entity.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class MyUserDetails implements UserDetailsService {
//导入userInfo对象的数据库代理对象
@Autowired
private UserInfoRepository userInfoRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//获取数据库中用户数据
UserInfo userInfo = userInfoRepository.findByUsername(username);
if (userInfo==null){
throw new UsernameNotFoundException("用户名不存在");
}
//给对象赋权限,如果需要创建角色,可以新建角色表,在这里查询角色权限,再将权限全部添加
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_"+userInfo.role));
//"ROLE_"一定要加
return new User(username,
userInfo.password,
authorities
);
}
}
12 创建MyWebSecurityConfig类,继承 WebSecurityConfigurerAdapter接口
配置访问资源权限,用户认证方式,登录方式
package com.jade.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity //启用该配置类
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法上加权限
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetails myUserDetails;
@Autowired
private PasswordEncoder passwordEncoder;
//资源限制权限
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/index").permitAll() //所有用户都已访问
.antMatchers("/main").hasRole("main") //main权限可以访问
.antMatchers("/user").hasRole("user") //user权限可以访问
// .anyRequest().authenticated() //所有请求都需要验证,登录即可
.and()
.formLogin() //开启使用表单登录
.loginPage("/login_on") //自定义登录页面
.failureForwardUrl("/login_on") //失败转发页面
.successForwardUrl("/") //成功转发页面
.and()
.logout() //配置退出登录
.and()
.rememberMe() //配置记住我
.and()
.csrf().disable();// post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数
}
//用户认证管理
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中添加用户信息,不在去数据库中读取,适合较小用户
/*
auth
.inMemoryAuthentication() //在内存中添加用户信息,
.withUser("supperMan").password(passwordEncoder.encode("123456")).roles("supper");
*/
//从数据中读取用户
auth.userDetailsService(myUserDetails).passwordEncoder(passwordEncoder);
}
}
13 新建TestController类,测试在方法上加权限
package com.jade.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("test")
public class TestController {
@RequestMapping("/user")
@ResponseBody
@PreAuthorize("hasAnyRole('user')") // 只能user角色才能访问该方法
public String userTest(){
return "你有User权限";
}
@RequestMapping("/main")
@ResponseBody
@PreAuthorize("hasAnyRole('main')") // 只能user角色才能访问该方法
public String mainTest(){
return "你有main权限";
}
}
14 设置html页面,测试交互逻辑,验证security
index.html
<body>
This is index
<p>
<a href="/main">main</a>
<a href="/user">user</a>
</p>
<p>
<a href="/logout">退出登录</a>
</p>
</body>
login.html
<form action="/login_on" method="post">
<p> username : <input name="username" type="text"> </p>
<p> password : <input name="password" type="text"> </p>
<p> <input type="submit" value="登录"> </p>
</form>
mian.html
<body>
this is main
</body>
user.html
<body>
this is user
</body>
总结
security框架的使用并不麻烦,相对于shiro,可能不太还理解。
我们在使用时只需要记住几个核心类即可
-
UserDetailsService :从数据库中加载user信息
-
WebSecurityConfigurerAdapter :配置security的核心信息
-
@EnableWebSecurity :启用该security配置类
-
@EnableGlobalMethodSecurity(prePostEnabled = true) :启用方法基于注解
-
@PreAuthorize(“hasAnyRole(‘user’)”) :修饰方法需要权限
项目原码
gitee地址
https://gitee.com/magical-ke-ke/security-student.git