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. 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>

注:本项目完整的pom文件稍后附上

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/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd">

    <!--spring过滤器链配置
        1) 需要拦截什么资源
        2) 什么资源对应什么角色权限
        3) 定制认证方式: HttpBasic or FormLogin
        4) 自定义登录页面,定义登录请求地址,定义错误处理方式
    -->
    <security:http>
        <!-- 使用http-basic的方式进行认证 -->
        <!--<security:http-basic/>-->
        <!-- 使用form-login的方式进行认证 -->
        <security:form-login/>

        <!-- 配置资源拦截规则
            pattern属性指定资源目录: 即需要拦截的资源  /* 代表根目录下的一级目录  /** 代表根目录下的所有目录
            access(SpEL)方法执行Spring EL表达式。提供如下表达式:
                permitALL():设置那些路径可以直接访问,不需要认证。直接返回true
                isAnonymous():只有匿名用户可以访问,登录用户不可访问
                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/javaee
   http://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.xml
      classpath: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/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/mvc
                        http://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/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd">

    <!--spring过滤器链配置
        1) 需要拦截什么资源
        2) 什么资源对应什么角色权限
        3) 定制认证方式: HttpBasic or FormLogin
        4) 自定义登录页面,定义登录请求地址,定义错误处理方式
    -->
    <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值,如果这里不配置,则默认为username
                password-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():设置那些路径可以直接访问,不需要认证。直接返回true
                isAnonymous():只有匿名用户可以访问,登录用户不可访问
                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:用户退出后要被重定向的url
        invalidate-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"/> &lt;!&ndash;数据库数据源&ndash;&gt;
        &lt;!&ndash;<property name="createTableOnStartup" value="true"/>&ndash;&gt; &lt;!&ndash;createTableOnStartup属性是当项目启动时,springSecurity创建表存储remember me相关信息,第二次启动时要注释这个属性&ndash;&gt;
    </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
  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return this.authorities;
  }

  public void setAuthorities(List<GrantedAuthority> authorities) {
    this.authorities = authorities;
  }

  // 返回用户密码,上面声明了属性 password
  @Override
  public String getPassword() {
    return password;
  }

  // 返回用户名,上面声明了属性 username
  @Override
  public String getUsername() {
    return username;
  }

  @Override
  public boolean isAccountNonExpired() {
    return accountnonexpired;
  }

  public void setAccountnonexpired(boolean accountnonexpired) {
    this.accountnonexpired = accountnonexpired;
  }

  @Override
  public boolean isAccountNonLocked() {
    return accountnonlocked;
  }

  public void setAccountnonlocked(boolean accountnonlocked) {
    this.accountnonlocked = accountnonlocked;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return credentialsnonexpired;
  }

  public void setCredentialsnonexpired(boolean credentialsnonexpired) {
    this.credentialsnonexpired = credentialsnonexpired;
  }

  @Override
  public 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_USER
        where 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 d
        where a.id = b.user_id and b.role_id = c.role_id and c.permission_id = d.id
          and 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 {

    @Autowired
    private SystemDao systemDao;

    @Override
    public 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接口
     */
    @Autowired
    private 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:表示认证成功后的信息
     */
    @Override
    public 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设置响应头为JSON
        httpServletResponse.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:表示认证失败后的信息
     */
    @Override
    public 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设置响应头为JSON
        httpServletResponse.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 {

    @Autowired
    private SystemDao systemDao;

    /**
     * loadUserByUsername:读取用户信息
     * 返回值类型 UserDetails 是 SpringSecurity 用来封装用户数据的接口
     */
    @Override
    public 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 就结束了。


下一篇讲讲用户认证登录进来以后如何根据角色获取菜单资源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/344043.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ffplay 之 Invalid data found when processing input

调试rtsp 通信协议时&#xff0c;发现使用 ffplay -i rtsp://127.0.0.1:554 tcp通信会先返回OPTIONS、DESCRIBE 2个指令&#xff0c;当返回SETUP指令给ffplay.exe程序时&#xff0c;会出现&#xff1a; 仔细查看代码&#xff0c;未支持UDP&#xff1a; 故,找到原因 ffplay -…

java web mvc-07-Vaadin 入门介绍

拓展阅读 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 …

计算机网络 第3章(数据链路层)

系列文章目录 计算机网络 第1章&#xff08;概述&#xff09; 计算机网络 第2章&#xff08;物理层&#xff09; 计算机网络 第3章&#xff08;数据链路层&#xff09; 文章目录 系列文章目录1. 数据链路层概述1.1 概述1.2 三个重要问题 2. 封装成帧2.1 介绍2.2 透明传输2.3 总…

深入理解——面向对象和面向过程

OOP 谈到面向对象逃不掉的一个问题就是面向对象和面向过程的区别和联系&#xff1a; 从时间的线性角度来说&#xff0c;面向对象是面向过程的下一个发展阶段&#xff0c;从二者的逻辑角度来说&#xff0c;则并没有纯粹的优劣&#xff0c;而是需要编码者根据特定的情况来选择特…

Parallels Desktop 18 for Mac(pd虚拟机) 激活版

Parallels Desktop 18是一款功能强大的虚拟机软件&#xff0c;可以在Mac操作系统上同时运行多种操作系统&#xff0c;包括Windows、Linux、Android等。该软件提供了多种高级功能&#xff0c;如支持DirectX 11游戏、3D图形和OpenGL应用程序&#xff0c;以及运行Windows和Mac应用…

一天吃透消息队列面试八股文

内容摘自我的学习网站&#xff1a;topjavaer.cn 为什么要使用消息队列&#xff1f; 总结一下&#xff0c;主要三点原因&#xff1a;解耦、异步、削峰。 1、解耦。比如&#xff0c;用户下单后&#xff0c;订单系统需要通知库存系统&#xff0c;假如库存系统无法访问&#xff0…

Stable Diffusion学习

参考 Stable Diffusion原理详解_stable diffusion csdn-CSDN博客 Stable Diffusion是stability.ai开源的图像生成模型&#xff0c;可以说Stable Diffusion的发布将AI图像生成提高到了全新高度&#xff0c;其效果和影响不亚于Open AI发布ChatGPT。 图像生成的发展 在Stable D…

【赠书第18期】人工智能B2B落地实战:基于云和Python的商用解决方案

文章目录 前言 1 方案概述 2 方案实施 2.1 云平台选择 2.2 Python环境搭建 2.3 应用开发与部署 2.4 应用管理 2.5 安全性与隐私保护 3 方案优势与效益 4 推荐图书 5 粉丝福利 前言 随着云计算技术的快速发展&#xff0c;越来越多的企业开始将业务迁移至云端&#x…

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效…

keil5 查看stm32 寄存器的值

1 查看芯片内部寄存器的值&#xff0c;首先是在仿真状态下&#xff0c;首先仿真&#xff0c;程序运行。 2 点击菜单栏的 View -> System viewer &#xff0c;右侧便会出现芯片的所有寄存器(如果没有&#xff0c;需要添加)&#xff0c;点击要查看的寄存器&#xff0c;便会出…

8. UE5 RPG创建UI(上)

UI是显示角色的一部分属性玩家可以直接查看的界面&#xff0c;通过直观的形式在屏幕上显示角色的各种信息。如何使用一种可扩展&#xff0c;可维护的形式来制作&#xff0c;这不得不说到耳熟能详的MVC架构。 MVC&#xff08;Model-View-Controller&#xff09;是一种常见的软件…

javaWebssh宠物基地管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh宠物基地管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用 B/S模式开发。开发环境为TOMCAT7.…

Unity | 渡鸦避难所-8 | URP 中利用 Shader 实现角色受击闪白动画

1. 效果预览 当角色受到攻击时&#xff0c;为了增加游戏的视觉效果和反馈&#xff0c;可以添加粒子等动画&#xff0c;也可以使用 Shader 实现受击闪白动画&#xff1a;受到攻击时变为白色&#xff0c;逐渐恢复为正常颜色 本游戏中设定英雄受击时播放粒子效果&#xff0c;怪物…

SG-8506CA 可编程晶体振荡器 (SPXO)

输出: LV-PECL频率范围: 50MHz ~ 800MHz电源电压: 2.5V to 3.3V外部尺寸规格: 7.0 5.0 1.5mm (8引脚)特性:用户指定一个起始频率, 7-bit I2C 地址:用户可编程: I2C 接口:基频的高频晶体:低抖动PLL技术应用:OTN, BTS, 测试设备 规格&#xff08;特征&#xff09; *1 这包括初…

软件工程实验报告(完整)

博主介绍&#xff1a;✌全网粉丝喜爱、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦&#xff01; &#x1f345;附上相关C语言版源码讲解&#x1f345; &#x1f44…

pikachu_ssrf攻略

ssrf&#xff08;curl&#xff09;&#xff1a; 打开pikachu靶场&#xff1a; http://127.0.0.1/pikachu-master/vul/ssrf/ssrf_curl.php?urlhttp://127.0.0.1/pikachu-master/vul/ssrf/ssrf_info/info1.php 发现URL地址最后面是info1.php 猜测一下有没有可能存在info2.php?…

java抽象工厂实战与总结

文章目录 一、工厂模式&#xff08;三种&#xff09;1.简单工厂模式1.1 概念&#xff1a;1.2 使用场景&#xff1a;1.3 模型图解&#xff1a;1.4 伪代码&#xff1a; 2.工厂方法模式2.1 概念&#xff1a;2.2 使用场景&#xff1a;2.3 模型图解&#xff1a;2.4 伪代码 3.抽象工厂…

C++逆向分析--虚函数(多态的前置)

先理解一件事&#xff0c;在intel汇编层面来说&#xff0c;直接调用和间接调用的区别。 直接调用语法&#xff1a; call 地址 硬编码为 &#xff1a;e8 间接调用语法: call [ ...] 硬编码为: FF 那么在C语法中&#xff0c;实现多态的前提是父类需要实现多态的成员…

深入分析 Linux 网络丢包问题

热门IT课程【视频教程】-华为/思科/红帽/oraclehttps://xmws-it.blog.csdn.net/article/details/134398330 所谓丢包&#xff0c;是指在网络数据的收发过程中&#xff0c;由于种种原因&#xff0c;数据包还没传输到应用程序中&#xff0c;就被丢弃了。这些被丢弃包的数量&#…

【分布式技术专题】「分布式技术架构」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)

探索Tomcat技术架构设计模式的奥秘 Tomcat系统架构分析Tomcat 整体结构Tomcat总体结构图以 Service 作为“婚姻”1) Service 接口方法列表 2) StandardService 的类结构图方法列表 3) StandardService. SetContainer4) StandardService. addConnector 以 Server 为“居”1) Ser…