手撸一个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. if结构题目记录

    1.使用if结构实现:若年龄够7岁或者年龄够5岁并且性别为男,就可以搬动桌子 import java.util.Scanner; /** * 使用if结构实现:若年龄够7岁或者年龄够5岁并且性别为男, ...

  2. HDOJ1573X问题

    https://acm.hdu.edu.cn/showproblem.php?pid=1573 n组线性同余方程求解,最后求出多少解.而最终的解的周期为最小公倍数,范围内的,需要这样算.如果最小超过, ...

  3. 蓝桥杯ALGO-1003

    问题描述 JiaoShou在爱琳大陆的旅行完毕,即将回家,为了纪念这次旅行,他决定带回一些礼物给好朋友. 在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子. 这些石子很漂亮,JiaoSh ...

  4. 我选择了MySQL和SpringData JPA

    我是3y,一年CRUD经验用十年的markdown程序员‍常年被誉为优质八股文选手 今天想跟大家聊聊数据库层面上的事,austin项目继续更新(注:今天聊的数据库都特指关系型数据库) 01.数据库选择 ...

  5. 《剑指offer》面试题32 - I. 从上到下打印二叉树

    问题描述 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印.   例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 返回 ...

  6. IPX下载安装

    IPX下载安装 该软件需要事先安装LAPACK与openblas,相关安装教程请点击链接. 1.下载 mkdir IPX cd IPX git clone https://github.com/ERG ...

  7. 【刷题-LeetCode】306. Additive Number

    Additive Number Additive number is a string whose digits can form additive sequence. A valid additiv ...

  8. 遇到奇怪的问题:web.py 0.40中使用web.input(),出现一堆奇怪的错误

    有的请求很正常,有的请求就出现了500错误. 这里使用POST请求,然后在web.input()中出现了很长很长的错误. 猜测是这个机器上安装了python2.7 / python 3.6 / pyt ...

  9. Unable to open 'free_base.cpp': Unable to read file 'c:\Program Files\Microsoft VS Code\minkernel\crts\ucrt\src\appcrt\heap\free_base.cpp'

    问题 vscode编写C++程序,使用microsoft C++ Unable to open 'cvt.cpp': Unable to read file 'c:\Program Files\Mic ...

  10. golang中的匿名函数三种用法

    package main import ( "fmt" "strconv" ) func main() { // 匿名函数的使用:方式1 f1 := func( ...