手撸一个springsecurity,了解一下security原理

转载自:www.javaman.cn 手撸一个springsecurity,了解一下security原理

今天手撸一个简易版本的springsecurity,带大家理解下springsecurity的原理:

安全框架的两个特点就是认证和授权,让我们来通过代码了解下认证和授权的处理方式:

1、认证

认证就是指需要登录才能进行系统操作,如:登录qq、微信或者是web系统的登录都是认证的过程

1.1 工程目录

1.2 maven配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dashi</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build> </project>

1.3 application.yml

spring:
mvc:
view:
prefix: /
suffix: .html

1.4 创建User,模拟springsecurity的用户信息的核心接口UserDetails

import lombok.AllArgsConstructor;
import lombok.Data; @Data
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}

1.5 创建AuthenticationService,模拟springsecurity的UserDetailsService核心接口

public interface AuthenticationService {
public User authentication(AuthenticationRequest authenticationRequest);
}

1.6 实现AuthenticationService,模拟实现UserDetailsService的过程

@Service
public class AuthenticationServiceImpl implements AuthenticationService {
private Map<String,User> users = new HashMap<String,User>();
@Override
public User authentication(AuthenticationRequest authenticationRequest) {
if(authenticationRequest == null||"".equals(authenticationRequest.getUsername())||"".equals(authenticationRequest.getPassword())){
throw new RuntimeException("用户名或密码为空");
}
User user = users.get(authenticationRequest.getUsername());
if(user == null){
throw new RuntimeException("用户不存在");
}
if(!user.getPassword().equals(authenticationRequest.getPassword())){
throw new RuntimeException("密码不正确");
}
return user; //模拟实现UserDetailS的User
} public User getUser(String username){
return users.get(username);
}
//创建两个账户,模拟管理员和普通的来宾用户
{
users.put("admin",new User(1,"admin","123")); //管理员
users.put("guest",new User(2,"guest","111")); //来宾账户
}
}

1.7 接收请求参数封装对象

@Data
public class AuthenticationRequest {
private String username;
private String password;
}

1.8 登录Controller,对/login请求处理,它调用AuthenticationService完成认证并返回登录结果提示信息

@Controller
public class LoginController { @Autowired
private AuthenticationService authenticationService; @GetMapping("/login")
public String login(){
return "login";
} @PostMapping(value = "/login",produces = {"text/plain;charset=UTF-8"})
public String login(AuthenticationRequest authenticationRequest, Model model){
System.out.println("111"+authenticationRequest);
User user = authenticationService.authentication(authenticationRequest);
String loginMsg = user.getUsername()+"登录成功";
System.out.println(loginMsg);
model.addAttribute("loginMsg",loginMsg);
return "loginsuccess";
}
}

1.9 认证页面

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>登录</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录1">
</form>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Title</title>
</head>
<body>
<h1 th:text="${loginMsg}"></h1>
</body>
</html>

1.10 认证成功后打印登录成功

1)登录界面如下

2)密码错误界面

3)登录成功界面

2、授权

授权就是控制什么样的用户或者角色访问什么功能,例如:管理员可以访问全部功能,guest普通用户只能访问某一个菜单或者功能

2.1 User增加权限authorities和session

package com.dashi.security.model;

import lombok.AllArgsConstructor;
import lombok.Data; import java.util.Set; @Data
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
//登录用户,增加登录用户session
public static final String LOGIN_USER = "user";
/**
* 用户权限
*/
private Set<String> authorities;
}

2.2 用户增加角色authorities

import com.dashi.security.model.AuthenticationRequest;
import com.dashi.security.model.User;
import com.dashi.security.service.AuthenticationService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set; @Service
public class AuthenticationServiceImpl implements AuthenticationService {
private Map<String,User> users = new HashMap<String,User>();
@Override
public User authentication(AuthenticationRequest authenticationRequest) {
if(authenticationRequest == null||"".equals(authenticationRequest.getUsername())||"".equals(authenticationRequest.getPassword())){
throw new RuntimeException("用户名或密码为空");
}
User user = users.get(authenticationRequest.getUsername());
if(user == null){
throw new RuntimeException("用户不存在");
}
if(!user.getPassword().equals(authenticationRequest.getPassword())){
throw new RuntimeException("密码不正确");
}
return user;
} public User getUser(String username){
return users.get(username);
} {
//增加管理员权限
Set<String> authorities1 = new HashSet<>();
authorities1.add("admin");
//增加来宾权限
Set<String> authorities2 = new HashSet<>();
authorities2.add("guest");
users.put("admin",new User(1,"admin","123",authorities1));
users.put("guest",new User(2,"guest","111",authorities2));
}
}

2.3登录成功后,将用户放到session中

import com.dashi.security.model.AuthenticationRequest;
import com.dashi.security.model.User;
import com.dashi.security.service.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpSession; @Controller
public class LoginController { @Autowired
private AuthenticationService authenticationService; @GetMapping("/login")
public String login(){
return "login";
} @PostMapping(value = "/login",produces = {"text/plain;charset=UTF-8"})
public String login(AuthenticationRequest authenticationRequest, Model model, HttpSession session){
System.out.println("111"+authenticationRequest);
User user = authenticationService.authentication(authenticationRequest);
session.setAttribute(User.LOGIN_USER,user);
String loginMsg = user.getUsername()+"登录成功";
System.out.println(loginMsg);
model.addAttribute("loginMsg",loginMsg);
return "loginsuccess";
}
}

2.4 增加Springboot拦截器配置,判断是admin用户,可以访问所有资源resource1和resource2,如果是guest用户只允许访问resource2资源

import com.dashi.security.model.User;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter; @Component
public class MyAuthenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object object = request.getSession().getAttribute(User.LOGIN_USER);
System.out.println("object = " + object);
if(object == null){
writeContent(response,"请登录");
}
//获取请求的url
String requestURI = request.getRequestURI();
User user = (User)object;
if(user.getAuthorities().contains("admin") && requestURI.contains("/resource1") || requestURI.contains("/resource2")){
writeContent(response,user.getUsername()+"访问:"+requestURI+"访问成功!");
return true;
}
if(user.getAuthorities().contains("guest") && requestURI.contains("/resource2")){
writeContent(response,user.getUsername()+"访问:"+requestURI+"访问成功!");
return true;
}
writeContent(response,"权限不足!");
return false;
} private void writeContent(HttpServletResponse response,String msg) throws IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter printWriter = response.getWriter();
printWriter.write(msg);
printWriter.close();
response.resetBuffer();
}
}

2.5 拦截器进行请求拦截,拦截/resource/**请求

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration
public class LoginConfig implements WebMvcConfigurer {
@Autowired
private MyAuthenInterceptor myAuthenInterceptor; public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(myAuthenInterceptor).addPathPatterns("/resource/**");
}
}

2.6 授权测试界面如下:

1)登录成功后设置resource1和resource2访问功能

2)admin用户访问资源1和资源2





3)guest用户只能访问资源2,不能访问资源1



2.7 实现了springsecurity的认证和授权

1、 认证功能:

loginPage("/login.html")即为认证

2、 授权功能:

.antMatchers("/resource/resource1").hasAuthority(“admin”)

.antMatchers("/resource/resource2").hasAuthority(“admin”)

.antMatchers("/resource/resource2").hasAuthority(“guest”)

  @Override
protected void configure(HttpSecurity http) throws Exception {
//以下五步是表单登录进行身份认证最简单的登录环境
http.formLogin() //表单登陆
.loginPage("/login.html") //指定登陆页面
.loginProcessingUrl("/authentication/form")//登陆页面提交的页面 开始使用UsernamePasswordAuthenticationFilter过滤器处理请求
.and() //
.authorizeRequests() //下面的都是授权的配置
.antMatchers("/resource/resource1").hasAuthority("admin")
.antMatchers("/resource/resource2").hasAuthority("admin") .antMatchers("/resource/resource2").hasAuthority("guest")
.and()
.csrf().disable();//关闭跨站请求伪造攻击拦截

手撸一个springsecurity,了解一下security原理的更多相关文章

  1. 手撸一个SpringBoot-Starter

    1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...

  2. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  3. 使用Java Socket手撸一个http服务器

    原文连接:使用Java Socket手撸一个http服务器 作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomc ...

  4. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  5. 第二篇-用Flutter手撸一个抖音国内版,看看有多炫

    前言 继上一篇使用Flutter开发的抖音国际版 后再次撸一个国内版抖音,大部分功能已完成,主要是Flutter开发APP速度很爽,  先看下图 项目主要结构介绍 这次主要的改动在api.dart 及 ...

  6. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

  7. 五分钟,手撸一个Spring容器!

    大家好,我是老三,Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开S ...

  8. Golang:手撸一个支持六种级别的日志库

    Golang标准日志库提供的日志输出方法有Print.Fatal.Panic等,没有常见的Debug.Info.Error等日志级别,用起来不太顺手.这篇文章就来手撸一个自己的日志库,可以记录不同级别 ...

  9. 以鶸ice为例,手撸一个解释器(一)明确目标

    代码地址 # HelloWorld.ice print("hello, world") 前言(废话) 其实从开始学习编译原理到现在已经有快半年的时间了,但是其间常常不能坚持看下去龙 ...

随机推荐

  1. 学习笔记--我的第一个Java程序

    我的第一个Java程序 // pubilc 表示公开的 // class 表示定义一个类 // HelloWorld 表示一个类名 public class HelloWorld { // 表示定义一 ...

  2. HDU 2044 一只小蜜蜂... (斐波那契数列)

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=2044 题目分析:其实仔细读题就会发现其中的规律, 其中:这是一个典型的斐波那契数列. 代码如下: #i ...

  3. 推荐召回--基于用户的协同过滤UserCF

    目录 1. 前言 2. 原理 3. 数据及相似度计算 4. 根据相似度计算结果 5. 相关问题 5.1 如何提炼用户日志数据? 5.2 用户相似度计算很耗时,有什么好的方法? 5.3 有哪些改进措施? ...

  4. FilterChain过滤器链(Servlet)

    在 Web 应用中,可以部署多个 Filter,若这些 Filter 都拦截同一目标资源,则它们就组成了一个 Filter 链(也称过滤器链).过滤器链中的每个过滤器负责特定的操作和任务,客户端的请求 ...

  5. Servlet Filter(过滤器)

    Servlet Filter 又称 Servlet 过滤器,它是在 Servlet 2.3 规范中定义的,能够对 Servlet 容器传给 Web 资源的 request 对象和 response 对 ...

  6. gin的源码解读4-gin的路由算法

    gin的路由算法 gin的是路由算法其实就是一个Trie树(也就是前缀树). 有关数据结构的可以自己去网上找相关资料查看. 注册路由预处理 我们在使用gin时通过下面的代码注册路由 普通注册 rout ...

  7. Casbin + Gin + Gorm 学习探索

    Casbin 是一个强大的,开源的访问控制框架,权限管理机制支持多种访问控制模型: 并且支持多种编程语言: 文档地址:https://casbin.org/docs/zh-CN/overview Gi ...

  8. linux下查看开放的端口

    Nmap是一款针对大型网络的端口扫描工具,它也适用于单机扫描,它支持很多扫描,也同时支持性能和可靠性统计. [root@localhost ~]# yum install namp [root@loc ...

  9. MySQL语句SQL应用

    目录 一:sql语句 1.什么是SQL语句? 二:基本SQL语句之库操作 三:基本SQL语句之表操作 1.查看当前所在库名称 2.切换数据库 四:基本SQL语句之记录操作 五:创建表的完整语法 一:s ...

  10. @ResponeBody 和 @RequestBody

    一.补充注解?1.@ResponseBody 将数据转成json 并输出到响应流中2.@RequestBody 将请求中的json数据转换成java对象.1.1 jsp页面 增添两个点击事件. 1.2 ...