门户网站建设单位资质要求/什么是百度权重
目录
前言
实战开发:
一、Spring Security整合到SSM项目
1. pom文件引入包
2. web.xml 配置
3. 添加 spring-security.xml 文件
二、Spring Security实战应用
1. 项目结构
2. pom文件引入
3. web.xml 配置
4. Spring 配置 applicationContext.xml
5. spring-security.xml 配置
6. springmvc.xml 配置
7. 创建实体类
8. DAO层实现数据查询
9. SystemDao 接口编写(数据层接口类)
10. SystemService接口及实现类SystemServiceImpl编写
11. SystemController 控制器编写
12. SpringSecurity实战讲解
13. 运行项目查看效果
前言
实战前提条件:
基础的SSM项目已集成完毕。在此基础上集成Spring Security实现web项目的安全保护 。
本文版本说明:
JDK:1.8
spring.version:5.2.12.RELEASE
Spring Security.version:4.2.5.RELEASE
Spring Security标签库:4.2.3.RELEASE
实战目标:
Authentication:认证,实现用户认证登录
Authorization:授权,设定用户的资源,访问权限。
实战开发:
一、Spring Security整合到SSM项目
1. pom文件引入包
<!-- Spring Security,此处引入4.2.5.RELEASE版本。因为spring security 5.X版本需要提供一个PasswordEncorder的实例,否则后台会报错。当然你也可以提供PasswordEncorder的实例 使用5.X版本--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>4.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>4.2.5.RELEASE</version></dependency><!-- Spring Security 标签库--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>4.2.3.RELEASE</version></dependency>
2. web.xml 配置
注:先说说web.xml配置文件中Spring家族的加载顺序。
先启动spring ioc容器 --> 再启动spring-security --> 然后启动springmvc
<!-- https://blog.csdn.net/qyb19970829/article/details/110100544 配置时注意关于spring容器加载顺序的问题,applicationContext.xml,spring-security.xml,springmvc.xml 即这几个配置文件加载顺序--><!-- SpringSecurity过滤器链 --><filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--配置Spring的监听器,启动spring容器--><display-name>Archetype Created Web Application</display-name><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!--配置加载类路径的配置文件,注意加载顺序 先加载spring--><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/applicationContext.xml <!-- Spring配置文件 -->classpath:spring/spring-security.xml <!-- SpringSecurity配置文件 --></param-value></context-param>
注:本项目完整的web.xml配置稍后附上
3. 添加 spring-security.xml 文件
注:本文件使用form-login的方式进行认证,在项目resources文件下新建spring文件夹(如果没有的话)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:security="http://www.springframework.org/schema/security"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd"><!--spring过滤器链配置1) 需要拦截什么资源2) 什么资源对应什么角色权限3) 定制认证方式: HttpBasic or FormLogin4) 自定义登录页面,定义登录请求地址,定义错误处理方式--><security:http><!-- 使用http-basic的方式进行认证 --><!--<security:http-basic/>--><!-- 使用form-login的方式进行认证 --><security:form-login/><!-- 配置资源拦截规则pattern属性指定资源目录: 即需要拦截的资源 /* 代表根目录下的一级目录 /** 代表根目录下的所有目录access(SpEL)方法执行Spring EL表达式。提供如下表达式:permitALL():设置那些路径可以直接访问,不需要认证。直接返回trueisAnonymous():只有匿名用户可以访问,登录用户不可访问isAuthenticated():需要身份认证成功才能访问。如果认证用户不是匿名用户,则返回true,认证通过isFullyAuthenticated():需要身份认证成功才能访问。如果认证用户不是匿名用户或记住我的用户,则返回true,认证通过其它自行查找......--><security:intercept-url pattern="/**" access="isAuthenticated()"/></security:http><!--身份验证管理器--><security:authentication-manager><!--自定义授权提供源,实际开发中提供 自定义用户详情查询获取接口--><security:authentication-provider><security:user-service><security:user name="admin" password="123456" authorities="ROLE_USER"/></security:user-service></security:authentication-provider></security:authentication-manager></beans>
以上为spring-security.xml 的基础配置,到此Spring Security整合到SSM项目中已经完毕!运行项目后,所有资源会被拦截,跳转到默认登录页要求用户进行登录认证后才能访问项目资源。如图:
二、Spring Security实战应用
1. 项目结构
2. pom文件引入
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wqbr</groupId><artifactId>wqdemotwo</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>wqdemotwo Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><spring.version>5.2.12.RELEASE</spring.version></properties><dependencies><!--spring 包--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.6.8</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><!-- Spring Security,此处引入4.2.5.RELEASE版本。因为spring security 5.X版本需要提供一个PasswordEncorder的实例,否则后台会报错。当然你也可以提供PasswordEncorder的实例 使用5.X版本--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>4.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>4.2.5.RELEASE</version></dependency><!-- Spring Security 标签库--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>4.2.3.RELEASE</version></dependency><!-- 引入jackson依赖包--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.4</version></dependency><!--JSP(Java Server Pages,Java服务端页面)--><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.0</version><scope>provided</scope></dependency><!-- JSTL标准标签库(Jsp Standarded Tag Library),使用标签取代JSP里的JAVA代码 --><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><!--servlet API--><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope></dependency><!-- junit测试包 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!--mybatis 相关包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.3.5</version></dependency><!--mybatis和spring集成的依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.7</version></dependency><!--oracle JDBC连接依赖--><dependency><groupId>com.oracle.database.jdbc</groupId><artifactId>ojdbc8</artifactId><version>21.3.0.0</version></dependency><dependency><groupId>cn.easyproject</groupId><artifactId>orai18n</artifactId><version>12.1.0.2.0</version></dependency><!--阿里的连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.12</version></dependency></dependencies><build><finalName>wqdemotwo</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>2.1.1</version><executions><execution><id>attach-sources</id><goals><goal>jar-no-fork</goal></goals></execution></executions><configuration><attach>true</attach></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>2.6</version><configuration><includeEmptyDirectories>true</includeEmptyDirectories></configuration></plugin></plugins></build>
</project>
3. web.xml 配置
配置web.xml,加载spring(applicationContext.xml --spring默认配置文件),加载spring-security,加载springmvc。
<?xml version="1.0" encoding="UTF-8"?><web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- https://blog.csdn.net/qyb19970829/article/details/110100544 配置时注意关于spring容器加载顺序的问题,applicationContext.xml,spring-security.xml,springmvc.xml 即这几个配置文件加载顺序--><!--字符编码过滤器一定要放在前面--><filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--不拦截所有是html的页面请求,weblogic中部署去掉<servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping>--><!-- SpringSecurity过滤器链 --><filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--配置Spring的监听器,启动spring容器--><!--配置加载类路径的配置文件,注意加载顺序--><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/applicationContext.xmlclasspath:spring/spring-security.xml</param-value></context-param><display-name>Archetype Created Web Application</display-name><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!--配置前端控制器,对浏览器发送的请求进行统一处理--><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--加载springmvc.xml配置文件的位置和名称,配置的是Spring配置--><init-param><!--contextConfigLocation:上下文配置路径,固定值--><param-name>contextConfigLocation</param-name><!--classpath:类路径,指的是Java和resources文件夹--><!--springmvc.xml:指的是配置文件的名称:需要配置springmvc.xml,在下面。spring默认配置文件为applicationContext.xml。当中配置spring创建容器时要扫描的包 已经整合到springmvc.xml中--><param-value>classpath:spring/springmvc.xml</param-value></init-param><!--配置启动加载--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!--开启项目时打开的页面-->
<!-- <welcome-file-list><welcome-file>/index.html</welcome-file></welcome-file-list>--></web-app>
4. Spring 配置 applicationContext.xml
指定扫描包,数据源,整合集成接管mybatis。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.1.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.1.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"><!--配置spring创建容器时要扫描的包--><!--同时也是 MyBatis托管的包路径--><!-- 扫描除了controller的所有bean 这里一定要 use-default-filters="true"--><context:component-scan base-package="com.wqbr" use-default-filters="true"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 引入配置文件--><bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="location" value="classpath:spring/jdbc.properties" /></bean><!--创建数据源 --><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></bean><!--<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiName"><value>testJNDI</value></property></bean>--><!--创建sqlSessionFactory,接管了mybatis配置文件。整合Mybatis--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="mapperLocations" ><array><!--映射在class编译路径下的TblsicardDao.xml全路径--><value>classpath:mapping/SystemDao.xml</value></array></property></bean><!--创建DAO,扫描mybatis接口的实现,加入到ioc容器中--><bean id="systemDao" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="sqlSessionFactory" ref="sqlSessionFactory"/><property name="mapperInterface" value="com.wqbr.persistence.SystemDao"/></bean></beans>
5. spring-security.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:security="http://www.springframework.org/schema/security"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd"><!--spring过滤器链配置1) 需要拦截什么资源2) 什么资源对应什么角色权限3) 定制认证方式: HttpBasic or FormLogin4) 自定义登录页面,定义登录请求地址,定义错误处理方式--><security:http><!--使用form-login的方式进行认证login-page:指定获取登录页面的url(需要编写controller返回登录页面)login-processing-url:指定登录页面中post请求提交到哪里的url(不需要编写controller,框架已实现)default-target-url:指定登录成功后,跳转到哪个url(需要编写controller)authentication-success-handler-ref:指定登录成功后,由哪个类来进行处理authentication-failure-handler-ref:指定登录失败后,由哪个类来进行处理username-parameter:指定登录表单中用户名的input中name值,如果这里不配置,则默认为usernamepassword-parameter:指定登录表单中密码的input中name值,如果这里不配置,则默认为password--><security:form-login login-page="/login" login-processing-url="/spring_security_check"authentication-success-handler-ref="myAuthenticationSuccessHandler"authentication-failure-handler-ref="myAuthenticationFailureHandler"/><!-- 关闭csrf的保护--><security:csrf disabled="true"/><!-- 配置资源拦截规则pattern属性指定资源目录: 即需要拦截的资源 /* 代表根目录下的一级目录 /** 代表根目录下的所有目录access(SpEL)方法执行Spring EL表达式。提供如下表达式:permitALL():设置那些路径可以直接访问,不需要认证。直接返回trueisAnonymous():只有匿名用户可以访问,登录用户不可访问isAuthenticated():需要身份认证成功才能访问。如果认证用户不是匿名用户,则返回true,认证通过isFullyAuthenticated():需要身份认证成功才能访问。如果认证用户不是匿名用户或记住我的用户,则返回true,认证通过其它自行查找......--><!--开始配置拦截规则,注意拦截规则的位置顺序(如不需要身份认证的规则,要放在前面,需要身份认证的规则放在后面)--><!--permitAll()不需要身份认证,无条件放行--><security:intercept-url pattern="/login" access="permitAll()"/><security:intercept-url pattern="/system/index" access="permitAll()"/><!--进行权限划分:hasRole('ROLE_USER'):表示拥有 ROLE_USER 权限的用户可以访问hasRole('ROLE_ALL'):表示拥有 ROLE_ALL 权限的用户可以访问--><security:intercept-url pattern="/system/add" access="hasAuthority('admin')"/><security:intercept-url pattern="/system/list" access="hasAuthority('ROLE_ALL')"/><!--permitAll()不需要身份认证,无条件放行静态资源--><security:intercept-url pattern="/js/**" access="permitAll()"/><!--拦截所有页面,需要身份认证成功才能访问。如果认证用户不是匿名用户或记住我的用户,则返回true,认证通过--><security:intercept-url pattern="/**" access="isFullyAuthenticated()"/><!--结束配置拦截规则--><!-- 自定义用户访问权限不足的处理方式(需要编写controller返回权限不足的页面) --><security:access-denied-handler error-page="/accessDeny"/><!--加上Remember Me功能,token-validity-seconds:有效时间(秒)--><!--<security:remember-me token-repository-ref="jdbcTokenRepository" token-validity-seconds="604800"/>--><!--<security:logout/>:注销功能logout-url="/logout":springSecurity内LogoutFilter要拦截的url(向这个url发送请求来注销)logout-success-url:用户退出后要被重定向的urlinvalidate-session:默认为true,用户在退出后Http session失效success-handler-ref:指定一个bean(需要实现LogoutSuccessHandler接口),用来自定义退出成功后的操作--><security:logout logout-url="/logout" logout-success-url="/login" invalidate-session="true"/></security:http><!--身份验证管理器--><security:authentication-manager><!--自定义授权提供类MyUserDetailsService,获得登录用户的用户详情信息。此类实现UserDetailsService接口。user-service-ref="myUserDetailsService" : 指定 UserDetailsService 接口的实现类最终都要返回一个UserDetail,用户详情--><security:authentication-provider user-service-ref="myUserDetailsService"><!-- 配置:加密算法对用户输入的密码进行加密,然后和数据库的密码进行配对 --><!--<security:password-encoder ref="bCryptPasswordEncoder"/>--></security:authentication-provider></security:authentication-manager><!--创建 springSecurity 密码加密工具类,使用PasswordEncoder 接口的实现,也可以使用别的--><!--<bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>--><!--springSecurity实现 remember me 功能:如果用户登录选择 remember me ,springSecurity会将其cookie值存入数据库,来实现remember me 功能JdbcTokenRepositoryImpl 用来存取cookie值--><!--<bean id="jdbcTokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl"><property name="dataSource" ref="dataSource"/> <!–数据库数据源–><!–<property name="createTableOnStartup" value="true"/>–> <!–createTableOnStartup属性是当项目启动时,springSecurity创建表存储remember me相关信息,第二次启动时要注释这个属性–></bean>--></beans>
6. springmvc.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"><!--配置spring创建容器时要扫描的包--><!-- 禁用默认扫描规则,use-default-filters="false"--><context:component-scan base-package="com.wqbr" use-default-filters="false"><!--只扫描Controller注解的类--><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><!--处理映射器--><bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/><!--处理器适配器--><bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/><!--配置JSP视图解析器--><bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/"></property> <!--规定跳转页面路径的前缀--><property name="suffix" value=".jsp"></property> <!--规定跳转页面的后缀--></bean><!-- 配置spring开启注解mvc的支持 默认就是开启的 ,要想让其他组件(不包含映射器、适配器、处理器)生效就必须需要配置了--><mvc:annotation-driven/><!-- 让默认servlet处理静态资源。将springMVC不能处理的请求交给servlet,一般用来放行静态资源 --><mvc:default-servlet-handler/></beans>
7. 创建实体类
注:重点在SysUser实体类。实现UserDetails接口 复写接口的方法进行实现,建立各方法的对应属性到用户表中(不一定全建对应属性)。
我们先来看下UserDetails接口类的源码:
红色标注的3项建立在用户实体领域类中,如下SysUser 用户实体类代码:
package com.wqbr.domain;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;/*** 系统用户,封装用户数据,实现 UserDetails 接口* @author lv* @date 2024年1月11日*/
public class SysUser implements UserDetails {private static final long serialVersionUID = 1L;private String id;private String username; //从UserDetails的重写方法中返回private String password; //从UserDetails的重写方法中返回private Date addtime;private boolean accountnonexpired; //账户是否过期,从UserDetails的重写方法中返回private boolean accountnonlocked; //账户是否锁定,从UserDetails的重写方法中返回private boolean credentialsnonexpired; //密码是否过期,从UserDetails的重写方法中返回private boolean enabled; //账户是否可用,从UserDetails的重写方法中返回// 储存用户拥有的所有权限private List<GrantedAuthority> authorities = new ArrayList<>(); //从UserDetails的重写方法中返回public String getId() {return id;}public void setId(String id) {this.id = id;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}public Date getAddtime() {return addtime;}public void setAddtime(Date addtime) {this.addtime = addtime;}// 返回用户权限,上面声明了权限集合对象 authorities@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}public void setAuthorities(List<GrantedAuthority> authorities) {this.authorities = authorities;}// 返回用户密码,上面声明了属性 password@Overridepublic String getPassword() {return password;}// 返回用户名,上面声明了属性 username@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return accountnonexpired;}public void setAccountnonexpired(boolean accountnonexpired) {this.accountnonexpired = accountnonexpired;}@Overridepublic boolean isAccountNonLocked() {return accountnonlocked;}public void setAccountnonlocked(boolean accountnonlocked) {this.accountnonlocked = accountnonlocked;}@Overridepublic boolean isCredentialsNonExpired() {return credentialsnonexpired;}public void setCredentialsnonexpired(boolean credentialsnonexpired) {this.credentialsnonexpired = credentialsnonexpired;}@Overridepublic boolean isEnabled() {return enabled;}public void setEnabled(boolean enabled) {this.enabled = enabled;}
}
注:private List<GrantedAuthority> authorities = new ArrayList<>(); 此属性稍后赋值演示
角色(SysRole )和资源(SysPermission)实体类代码参见以下文章建立: spirng框架之spring security(二)insert 语句补充-CSDN博客https://blog.csdn.net/u011529483/article/details/135467110?spm=1001.2014.3001.5501
8. DAO层实现数据查询
SystemDao.xml,mybatis的mapper文件(映射SQL语句)
<?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">
<!--命名空间映射到com.wqbr.persistence.SystemDao 类-->
<mapper namespace="com.wqbr.persistence.SystemDao"><!--SysUser findByUsername(String username);方法的映射对应id="findByUsername",resultType返回类型为SysUser实体类--><select id="findByUsername" parameterType="String" resultType="com.wqbr.domain.SysUser">select *from SYS_USERwhere USERNAME = #{username}</select><!--查询当前用户拥有的资源--><select id="findPermissionByUsername" parameterType="String" resultType="com.wqbr.domain.SysPermission">select d.*from sys_user a, sys_user_role b, sys_role_permission c, sys_permission dwhere a.id = b.user_id and b.role_id = c.role_id and c.permission_id = d.idand a.username = #{username}</select>
</mapper>
9. SystemDao 接口编写(数据层接口类)
package com.wqbr.persistence;import com.wqbr.domain.SysPermission;
import com.wqbr.domain.SysUser;import java.util.List;public interface SystemDao {/*** 查询当前用户对象*/public SysUser findByUsername(String username);/*** 查询当前用户拥有的资源*/public List<SysPermission> findPermissionByUsername(String username);
}
10. SystemService接口及实现类SystemServiceImpl编写
package com.wqbr.service;import com.wqbr.domain.SysPermission;import java.util.List;/*** 系统服务接口* @author lv* @date 2024年1月11日*/
public interface SystemService {/*** 查询当前用户拥有的资源*/public List<SysPermission> findPermissionByUsername(String username);
}
package com.wqbr.service.impl;import com.wqbr.domain.SysPermission;
import com.wqbr.persistence.SystemDao;
import com.wqbr.service.SystemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/*** 系统服务接口实现* @author lv* @date 2024年1月11日*/
@Service
public class SystemServiceImpl implements SystemService {@Autowiredprivate SystemDao systemDao;@Overridepublic List<SysPermission> findPermissionByUsername(String username) {return systemDao.findPermissionByUsername(username);}
}
11. SystemController 控制器编写
package com.wqbr.controller;import com.wqbr.domain.Menus;
import com.wqbr.domain.SysPermission;
import com.wqbr.domain.SysUser;
import com.wqbr.service.SystemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;/*** 系统用户控制器* @author lv* @date 2024年1月11日*/
@Controller
@RequestMapping("/system")
public class SystemController {/*** 自动装配SystemService接口*/@Autowiredprivate SystemService systemService;/*** 处理超链接发送出来的请求* @param model* @return*/@RequestMapping(path = "/hello")public String sayHello(Model model){System.out.println("入门方法执行了2...");// 配置了视图解析器后,写法return "main/index";}@RequestMapping(path = "/haa")public String haa(Model model){System.out.println("haa *****bb 2 999999999999999999...");// 向模型中添加属性msg与值,可以在html页面中取出并渲染//model.addAttribute("msg","hello,SpringMVC");// 配置了视图解析器后,写法return "main/index";}@RequestMapping(path = "/index")public String index(){System.out.println("index 页面进入......");return "main/index";}@RequestMapping(path = "/list")public String list(){System.out.println("list方法进入......");return "main/list";}@RequestMapping(path = "/add")public String add(){System.out.println("add方法进入......");return "main/add";}@GetMapping("/findMenu")public ModelAndView findMenus(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {ModelAndView model = new ModelAndView("main/menu");SysUser user = (SysUser) authentication.getPrincipal();String username=user.getUsername();if(username!=null){List<Menus> listMenu = new ArrayList<>();List<SysPermission> pList = systemService.findPermissionByUsername(username);System.out.println("=-----=大小为:"+pList.size());for (SysPermission permission : pList) {if (permission.getResource_type().equals("menu")) {Menus menu = new Menus();menu.setId(Long.parseLong(permission.getId()));menu.setName(permission.getName());menu.setParentId(Long.parseLong(permission.getParent_id()));menu.setParentIds(Long.parseLong(permission.getParent_ids()));menu.setUrl(permission.getUrl());listMenu.add(menu);}}//request.setAttribute("listMenus", listMenu);model.addObject("listMenu",listMenu);// for (Menus menus : listMenu) {
// if (menus.getParentId() == 10000) { //10000为数据库中的值
// System.out.println("==" + menus.getName() + "[" + menus.getUrl() + "]");
// for (Menus menusch : listMenu) {
// if (menus.getId() == menusch.getParentId()) {
// System.out.println("---------" + menusch.getName() + "[" + menusch.getUrl() + "]");
// }
// }
// }
// }}return model;}
}
12. SpringSecurity实战讲解
现在请查看之前配置的spring-security.xml文件。
如图 spring-security.xml 文件中给出了 security:form-login 的4个属性。并禁用了 csrf 。且指定了 error-page 的路径。所以需要编写 Controller 实现 login-page 及 error-page。
- LoginController 控制器编写
package com.wqbr.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;/*** 系统用户控制器* @author lv* @date 2024年1月16日*/
@Controller
public class LoginController {@RequestMapping("/login")public String login(){System.out.println("初始 指定 进入登录页面!。。。。。。。。。。。。。。");return "login";}/*** 自定义用户访问权限不足的处理方式(需要编写controller返回权限不足的页面)* @return*/@RequestMapping("/accessDeny")public String accessDeny(){System.out.println("自定义用户访问权限不足的处理方式(需要编写controller返回权限不足的页面)。。。。。。。。。。。。。。");return "accessdeny";}
}
- 编写 MyAuthenticationSuccessHandler 与 MyAuthenticationFailureHandler 类实现spring-security.xml 文件中 authentication-success-handler-ref 与 authentication-failure-handler-ref 属性指定的接口。(以json格式返回成功或失败)
package com.wqbr.service.impl;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Service;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;@Service
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {// new 一个 jackson 的 对象private ObjectMapper objectMapper = new ObjectMapper();/*** 此方法会在登录成功后进行回调** @param authentication:表示认证成功后的信息*/@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {// 自己构造json字符串,返回给前端Map<String,Object> result = new HashMap<>();result.put("authStr", "success");String json = objectMapper.writeValueAsString(result);// 使用response设置响应头为JSONhttpServletResponse.setContentType("text/json;charset=utf-8");// 回写数据httpServletResponse.getWriter().write(json);}
}
package com.wqbr.service.impl;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Service;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;@Service
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {// new 一个 jackson 的 对象private ObjectMapper objectMapper = new ObjectMapper();/*** 此方法会在登录失败后进行回调** @param authenticationException:表示认证失败后的信息*/@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authenticationException) throws IOException, ServletException {// 自己构造json字符串,返回给前端Map<String,Object> result = new HashMap<>();result.put("authStr", "fail");String json = objectMapper.writeValueAsString(result);// 使用response设置响应头为JSONhttpServletResponse.setContentType("text/json;charset=utf-8");// 回写数据httpServletResponse.getWriter().write(json);}
}
- 编写login.jsp页面,登录提交路径为 spring-security.xml 中指定的 login-processing-url="/spring_security_check" 路径。且运行项目后会根据 spring-security.xml 中的 login-page="/login" 访问 LoginController 控制器的方法跳转到 login.jsp 页。
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<%@ page isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>登录页面</title>
</head>
<body>
<h3>用户登录</h3>
<form action="${pageContext.request.contextPath}/spring_security_check" method="post">用户名:<input type="text" name="username"/><br>用户密码:<input type="password" name="password"/><br><input type="submit" value="登 录"/>
</form>
</body>
</html>
- spring-security.xml文件中的拦截规则如图:
- spring-security.xml文件中的用户详情接口实现(自定义登录授权实现类)
MyUserDetailsService类实现UserDetailsService接口,重写loadUserByUsername方法,实现此方法(用户登录时调用此方法,通过用户输入的登录信息查找数据库用户表进行身份认证匹配)
package com.wqbr.service.impl;import com.wqbr.domain.SysPermission;
import com.wqbr.domain.SysUser;
import com.wqbr.persistence.SystemDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;import java.util.List;/*** 系统用户控制器* @author lv* @date 2024年1月16日*/
@Service
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate SystemDao systemDao;/*** loadUserByUsername:读取用户信息* 返回值类型 UserDetails 是 SpringSecurity 用来封装用户数据的接口*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println("-------loadUserByUsername方法加载中。。。。username:"+username);/*** name: 用户名* password: 密码* authorities: 定义权限名称*/
// User user = new User("admin", "123456",
// AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_ALL"));
// return user;SysUser user=systemDao.findByUsername(username);System.out.println("user-====="+user);//判断if(user!=null){System.out.println(user.getUsername()+"---====---"+user.getPassword()+"----"+user.getAddtime());
/* List<SysPermission> permList=systemDao.findPermissionByUsername(user.getUsername());StringBuffer sb = new StringBuffer();for (SysPermission sysPermission : permList) {sb.append(sysPermission.getUrl());sb.append(",");}sb.delete(sb.length()-1,sb.length());*/List<GrantedAuthority> list=AuthorityUtils.createAuthorityList("ROLE_USER","admin");user.setAuthorities(list); //设置权限列表return user;}throw new UsernameNotFoundException(username+"用户名不存在!");}
}
到此spring-security讲解完毕,接下来补全几个测试页面
accessdeny.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
用户访问权限不足!。。。。。。。。。。。。。。。。。。。。。。。
</body>
</html>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<%@ page isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>welcome!</title>
</head>
<body>
<h3>welcome! index页面</h3>
<form action="#" method="post">
</form>
</body>
</html>
add.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h2>add...! 内测页面 </h2>
</body>
</html>
list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h2>list...! 列表 显示 </h2>
<h3>list...! || 列表 显示 </h3>
</body>
</html>
13. 运行项目查看效果
登录
输入错误的用户名、密码
输入正确的用户名、密码
登录成功后尝试访问/system/add 和 /system/list 方法请求
如图 /system/add 成功访问,/system/list 无法访问,因为权限不足。如下图用户详情类中没有给用户指定 ROLE_ALL 权限
现在我们关闭浏览器,重新打开浏览器。不登录的情况下访问控制器的 /system/add 请求 和 /system/index 请求。
访问 http://localhost:8080/wqdemotwo_war/system/add
跳回到登录页面。
访问 http://localhost:8080/wqdemotwo_war/system/index
成功访问。
好了关于 spring-security 就结束了。
下一篇讲讲用户认证登录进来以后如何根据角色获取菜单资源
相关文章:

SSM项目集成Spring Security 4.X版本(使用spring-security.xml 配置文件方式)
目录 前言 实战开发: 一、Spring Security整合到SSM项目 1. pom文件引入包 2. web.xml 配置 3. 添加 spring-security.xml 文件 二、Spring Security实战应用 1. 项目结构 2. pom文件引入 3. web.xml 配置 4. Spring 配置 applicationContext.xml 5. sp…...

如何生成开发语言的排名图表
1、解释说明 生成开发语言排名图表,通常需要以下几个步骤: - 首先,我们需要收集一些关于不同编程语言的统计数据,例如使用人数、市场份额等。这些数据可以从各种来源获取,例如网站、报告、数据库等。 - 然后&#x…...

有哪些简单好用、适合中小型企业的CRM系统?
阅读本文,你将了解:一、中小型企业对CRM系统的主要需求;二、盘点四款好用的CRM系统;三、CRM系统实施策略和优秀实践。 在快速变化的商业环境中,中小型企业面临着独特的挑战:如何在有限的资源下高效地管理客…...

Unity 适配器模式(实例详解)
文章目录 简介1. **Input Adapter 示例**2. **Component Adapter 示例**3. **网络数据解析适配器**4. **物理引擎适配**5. **跨平台服务适配** 简介 Unity中的适配器模式(Adapter Pattern)主要用于将一个类的接口转换为另一个接口,以便于原本…...

Spring boot项目java bean和xml互转
Spring boot项目实现java bean和xml互转 项目场景:互转方法使用jackson进行互转使用jaxws进行xml与bean的互转 搞定收工! 项目场景: 工作中需要给下游第三方收费系统做数据挡板,由于下游系统使用的是soap webservice,里面涉及各种…...

数字证书和数字证书认证机构和数字根证书,CA,RCA
文章目录 一、 数字证书1、什么是数字证书2、数字证书干什么的3、风险 二、数字证书认证机构(Certificate Authority,缩写为CA)参考文章 一、 数字证书 维基百科 公开密钥认证(英语:Public key certificateÿ…...

java web mvc-08-Grails 入门介绍
拓展阅读 Spring Web MVC-00-重学 mvc mvc-01-Model-View-Controller 概览 web mvc-03-JFinal web mvc-04-Apache Wicket web mvc-05-JSF JavaServer Faces web mvc-06-play framework intro web mvc-07-Vaadin web mvc-08-Grails 开源 The jdbc pool for java.(java …...

深度学习技术栈 —— Pytorch之TensorDataset、DataLoader
深度学习技术栈 —— Pytorch之TensorDataset、DataLoader 前言一、TensorDataset、DataLoader的用法?二、从.csv文件-->tensor张量总结 前言 简单来说,TensorDataset与DataLoader这两个类的作用, 就是将数据读入并做整合,以便…...

远程git开发
两种本地与远程仓库同步 """ 1)你作为项目仓库初始化人员:线上要创建空仓库 > 本地初始化好仓库 > 建立remote链接(remote add) > 提交本地仓库到远程(push)2)你作为项目后期开发人员:远程项目仓库已经创…...

Codeforces Round 812 (Div. 2) ---- C. Build Permutation --- 题解
目录 C. Build Permutation 题目描述: 编辑 思路解析: 代码实现: C. Build Permutation 题目描述: 思路解析: 先证明在任何情况下答案均存在。 假设我们所求的为 m m1 m2.....n 的排列,我们称不小于n…...

Matlab 将工作区变量保存到文件中(save)
语法 1、save(filename) 2、save(filename,variables) 3、save(filename,variables,fmt) 4、save(filename,variables,version) 5、save(filename,variables,version,-nocompression) 6、save(filename,variables,-append) 7、save(filename,variables,-append,-nocompression…...

源码实现简介
本系列所有代码在文章底部,每一章节代码可独立编译运行 随着科技的飞速发展,自动驾驶技术正逐渐成为现实。而在自动驾驶技术中,感知是至关重要的一个环节。通过感知,自动驾驶车辆能够识别和理解周围环境,进而做出相应…...

我每天如何使用 ChatGPT
我们都清楚互联网的运作方式——充斥着各种“爆款观点”,极端分裂的意见,恶搞和无知现象屡见不鲜。 最近,大家对于人工智能(AI)特别是大语言模型(LLMs)和生成式 AI(GenAI࿰…...

MySQL修炼手册14:用户权限管理:安全保障与数据隔离
目录 写在开头1 用户与权限的关系1.1 用户的创建与删除1.1.1 创建新用户1.1.2 批量创建用户1.1.3 安全删除用户 1.2 授予与撤销权限1.2.1 授予权限1.2.2 批量授予权限1.2.3 撤销权限 2 角色的应用2.1 创建与管理角色2.1.1 创建角色2.1.2 管理角色 2.2 将权限赋予角色2.2.1 将权…...

动态规划解决马尔可夫决策过程
马尔可夫决策过程是强化学习中的基本问题模型之一,而解决马尔可夫决策过程的方法我们统称为强化学习算法。 动态规划( dynamic programming, DP )具体指的是在某些复杂问题中,将问题转化为若干个子问题,并在求解每个子…...

ubuntu1604安装及问题解决
虚拟机安装vmbox7 虚拟机操作: 安装增强功能 sudo mkdir /mnt/share sudo mount -t vboxsf sharefolder /mnt/share第一次使用sudo提示is not in the sudoers file. This incident will be reported 你的root需要设置好密码 sudo passwd root 输入如下指令&#x…...

Leetcode—24. 两两交换链表中的节点【中等】
2023每日刷题(八十七) Leetcode—24. 两两交换链表中的节点 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x),…...

USRP相关报错解决办法
文章目录 前言一、本地环境二、相关报错信息二、解决办法1、更换电脑操作系统2、升级最新版固件 前言 在进行 USRP 开发时遇到了一些报错,这里做个记录解决问题的方法。 一、本地环境 电脑操作系统:Windows11MATLAB 版本:MATLAB 2021aUSRP …...

【剑指offer】重建二叉树
👑专栏内容:力扣刷题⛪个人主页:子夜的星的主页💕座右铭:前路未远,步履不停 目录 一、题目描述1、题目2、示例 二、题目分析1、递归2、栈 一、题目描述 1、题目 剑指offer:重建二叉树 给定节…...

中仕教育:事业编招考全流程介绍
一、报名阶段 1. 了解查看招聘信息:查看各类事业编岗位的招聘信息,包括岗位职责、招聘条件、报名时间等。 2. 填写报名表:按照要求填写报名表,包括个人信息、教育背景、工作经历等内容。 3. 提交报名材料:将报名表及…...

149. 直线上最多的点数
149. 直线上最多的点数 class MaxPoints:"""149. 直线上最多的点数https://leetcode.cn/problems/max-points-on-a-line/description/?envTypestudy-plan-v2&envIdtop-interview-150"""def solution(self, points: List[List[int]]) ->…...

不合格机器人工程讲师再读《悉达多》-2024-
一次又一次失败的经历,让我对经典书籍的认同感越来越多,越来越觉得原来的自己是多么多么的无知和愚昧。 ----zhangrelay 唯物也好,唯心也罢,我们都要先热爱这个世界,然后才能在其中找到自己所热爱的事业。 ----zh…...

【STM32CubeMX串口通信详解】USART2 -- DMA发送 + DMA空闲中断 接收不定长数据
( 本篇正在编写、更新状态中.....) 文章目录: 前言 前言 本篇,详细地用截图解释 CubeMX 对 USART2 的配置,HAL函数使用,和收发程序的编写。 收、发机制:DMA发送 DAM空闲中断接收。 DMA空…...

Webpack5入门到原理19:React 脚手架搭建
开发模式配置 // webpack.dev.js const path require("path"); const ESLintWebpackPlugin require("eslint-webpack-plugin"); const HtmlWebpackPlugin require("html-webpack-plugin"); const ReactRefreshWebpackPlugin require("…...

苹果眼镜(Vision Pro)的开发者指南(6)-实战应用场景开发 - 游戏、协作、空间音频、WebXR
第一部分:【构建游戏和媒体体验】 了解如何使用visionOS在游戏和媒体体验中创建真正身临其境的时刻。游戏和媒体可以利用全方位的沉浸感来讲述令人难以置信的故事,并以一种新的方式与人们联系。将向你展示可供你入门的visionOS游戏和叙事开发途径。了解如何使用RealityKit有…...

flutter底层架构初探
本文出处:Flutter 中文开发者网站 架构 embedder嵌入层 提供程序入口(其他原生应用也采用此方式),程序由此和底层操作系统协调(surface渲染、辅助功能和输入服务,管理事件循环…...

初识SQL注入
目录 注入攻击 SQL注入 手工注入 Information_schema数据库 自动注入 介绍一下这款工具:sqlmap 半自动注入 前面给大家通过学习练习的方式将XSS攻击的几种形式和一些简单的靶场和例题的演示,从本篇开始我将和小伙伴们通过边复习、边练习的方式来进…...

React初探:从环境搭建到Hooks应用全解析
React初探:从环境搭建到Hooks应用全解析 一、React介绍 1、React是什么 React是由Facebook开发的一款用于构建用户界面的JavaScript库。它主要用于构建单页面应用中的UI组件,通过组件化的方式让开发者能够更轻松地构建可维护且高效的用户界面。 Reac…...

设计模式——1_6 代理(Proxy)
诗有可解不可解,若镜花水月勿泥其迹可也 —— 谢榛 文章目录 定义图纸一个例子:图片搜索器图片加载搜索器直接在Image添加组合他们 各种各样的代理远程代理:镜中月,水中花保护代理:对象也该有隐私引用代理:…...

性能优化(CPU优化技术)-NEON 介绍
「发表于知乎专栏《移动端算法优化》」 本节主要介绍基本 SIMD 及其他的指令流与数据流的处理方式,NEON 的基本原理、指令以及与其他平台及硬件的对比。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:…...