SSM整合SpringSecurity简单使用

news2024/11/17 20:39:51

一、SpringSecurity

1.1 什么是SpringSecurity

Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。(官网地址)
Spring Security 为基于J2EE企业应用软件提供了全面安全服务。特别是使用领先的J2EE解决方案-Spring框架开发的企业软件项目。人们使用Spring Security有很多种原因,不过通常吸引他们的是在J2EE Servlet规范或EJB规范中找不到典型企业应用场景的解决方案。特别要指出的是他们不能再

WAR 或 EAR 级别进行移植。这样,如果你更换服务器环境,就要,在新的目标环境进行大量的工作,对你的应用

系统进行重新配置安全。使用Spring Security 解决了这些问题,也为你提供很多有用的,完全可以指定的其他安

全特性。安全包括两个主要操作

  • “认证”,是为用户建立一个他所声明的主体。主体一般是指用户,设备或可以在你系统中执行动作的其他系

统。(可以将主体当前权限框架自己的session,认证其实就是登录操作,并将登录成功的数据信息存入主体)

  • “授权”,指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由身份验证

过程建立了。(查询是否对应权限,授权其实就是在认证之后请求需要权限的资源时,查询数据库在主体中保存对应权限数据)

这些概念是通用的,不是Spring Security特有的。在身份验证层面,Spring Security广泛支持各种身份验证模式,

这些验证模型绝大多数都由第三方提供,或则正在开发的有关标准机构提供的,例如 Internet Engineering Task

Force.作为补充,Spring Security 也提供了自己的一套验证功能。

Spring Security 目前支持认证一体化如下认证技术:

HTTP BASIC authentication headers (一个基于IEFT RFC 的标准)

HTTP Digest authentication headers (一个基于IEFT RFC 的标准)

HTTP X.509 client certi?cate exchange(一个基于IEFT RFC 的标准)

LDAP (一个非常常见的跨平台认证需要做法,特别是在大环境)

Form-based-authentication (提供简单用户接口的需求)

OpenID authentication Computer Associates Siteminder JA-SIG Central Authentication Service (CAS,这是一个流行的开源单点登录系统)

Transparent authentication context propagation for Remote Method Invocation and HttpInvoker (一个Spring远程调用协议)

1.2 核心组件

SecurityContextHolder

SecurityContextHolder它持有的是安全上下文 (security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权等等,这些都被保存在SecurityContextHolder中。SecurityContextHolder默认使用ThreadLocal 策略来存储认证信息。看到 ThreadLocal 也就意味着,这是一种与线程绑定的策略。在web环境下,Spring Security在用户登录时自动绑定认证信息到当前线程,在用户退出时,自动清除当前线程的认证信息

看源码他有静态方法

//获取 上下文
  public static SecurityContext getContext() {
        return strategy.getContext();
    }
  //清除上下文  
  public static void clearContext() {
        strategy.clearContext();
    }

SecurityContextHolder.getContext().getAuthentication().getPrincipal()
getAuthentication() 返回了认证信息,

getPrincipal() 返回了身份信息

UserDetails 便是Spring对身份信息封装的一个接口

SecurityContext

安全上下文,主要持有 Authentication 对象,如果用户未鉴权,那Authentication对象将会是空的。看源码可知

package org.springframework.security.core.context;

import java.io.Serializable;
import org.springframework.security.core.Authentication;

public interface SecurityContext extends Serializable {
    Authentication getAuthentication();
    void setAuthentication(Authentication var1);
}

Authentication

鉴权对象,该对象主要包含了用户的详细信息 (UserDetails) 和用户鉴权时所需要的信息,如用户提交的用户名密码、Remember-me Token,或者digest hash值等,按不同鉴权方式使用不同的 Authentication 实现,看源码可知道

package org.springframework.security.core;

import java.io.Serializable;
import java.security.Principal;
import java.util.Collection;

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials();
    Object getDetails();
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean var1) throws IllegalArgumentException;

}

注意 GrantedAuthority 该接口表示了当前用户所拥有的权限(或者角色)信息。这些信息由授权负责对象 AccessDecisionManager 来使用,并决定最终用户是否可以访问某 资源(URL或方法调用或域对象)。鉴权时并不会使用到该对象

UserDetails

这个接口规范了用户详细信息所拥有的字段,譬如用户名、密码、账号是否过期、是否锁定等。在Spring Security中,获取当前登录的用户的信息,一般情况是需要在这个接口上面进行 扩展,用来对接自己系统的用户,看源码可知

package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

UserDetailsService

这个接口只提供一个接口 loadUserByUsername(String username) ,这个接口非常 重要, 一般情况我们都是通过 扩展 这个接口来显示获取我们的用户信息,用户登陆时传递的用户名和密码也是通过这里这查找出来的用户名和密码进行校验,但是真正的校验不在这里,而是由 AuthenticationManager 以及 AuthenticationProvider 负责的,需要强调的是,如果用户不存在,不应返回 NULL,而要抛出异常 UsernameNotFoundException,看源码可知

package org.springframework.security.core.userdetails;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

1.3 SpringSecurity快速入门

创建测试web工程

请添加图片描述

导入依赖

<dependencies>
    <!--springMVC-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>
    <!--servlet-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    <!--jsp-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
    <!--jstl-->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!--jsckson -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.9</version>
    </dependency>
    <!--文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>

    <!--spring相关坐标-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>

    <!--mybatis相关坐标-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>
    <!--数据库连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.15</version>
    </dependency>
    <!--mybatis-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <!--spring-mybatis坐标-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.2</version>
    </dependency>
    <!--lombok-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
    </dependency>
    <!--分页插件-->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.1.10</version>
    </dependency>

    <!--junit单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

    <!--spring-security-->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-core</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-taglibs</artifactId>
      <version>5.2.9.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>jsr250-api</artifactId>
      <version>1.0</version>
    </dependency>
    <!--log4j依赖-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
  </dependencies>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
           version="3.0">

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-security.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<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>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
</web-app>

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


    <security:http auto-config="true" use-expressions="false" >
        <!--
        intercept-url定义一个过滤规则pattern表示对哪些url进行权限控制,ccess属性表示在请求对应的URL时需要什么权限,
        默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL-->
        <security:intercept-url pattern="/**" access="ROLE_USER" />

        <!--<security:form-login />-->
    </security:http>
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{noop}user"
                               authorities="ROLE_USER" />
                <security:user name="admin" password="{noop}admin"
                               authorities="ROLE_ADMIN" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

测试

在这里插入图片描述

1.4 SpringSecurity 使用自定义页面

添加以下页面

在这里插入图片描述

修改SpringSecurity.xml配置文件

在这里插入图片描述

<!-- 配置不过滤的资源(静态资源及登录相关) -->
<security:http security="none" pattern="/login.html" />
<security:http security="none" pattern="/failer.html" />
<security:http auto-config="true" use-expressions="false" >
    <!-- 配置资料连接,表示任意路径都需要ROLE_USER权限 -->
    <security:intercept-url pattern="/**" access="ROLE_USER" />
    <!-- 自定义登陆页面,login-page 自定义登陆页面 authentication-failure-url 用户权限校验失败之后才会跳转到这个页面,如果数据库中没有这个用户则不会跳转到这个页面。
            default-target-url 登陆成功后跳转的页面。 注:登陆页面用户名固定 username,密码 password,action:login -->
    <security:form-login login-page="/login.html"
                         login-processing-url="/login" 
                         username-parameter="username"
                         password-parameter="password" authentication-failure-url="/failer.html"
                         default-target-url="/success.html" authentication-success-forward-url="/success.html"
                         />
    <!-- 关闭CSRF,默认是开启的 -->
    <security:csrf disabled="true" />
</security:http>
<security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{noop}user"
                               authorities="ROLE_USER" />
                <security:user name="admin" password="{noop}admin"
                               authorities="ROLE_ADMIN" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>

二、 用户登录

2.1 表结构分析与创建

在这里插入图片描述

-- 用户表
CREATE TABLE users(
	id VARCHAR(32) PRIMARY KEY,
	email VARCHAR(50) UNIQUE NOT NULL,
	username VARCHAR(50),
	PASSWORD VARCHAR(100),
	phoneNum VARCHAR(20),
	STATUS INT
);

-- 角色表
CREATE TABLE role(
    id VARCHAR(32) PRIMARY KEY,
    roleName VARCHAR(50) ,
    roleDesc VARCHAR(50)
);


-- 用户角色关联表
CREATE TABLE users_role(
    userId VARCHAR(32),
    roleId VARCHAR(32),
    PRIMARY KEY(userId,roleId),
    FOREIGN KEY (userId) REFERENCES users(id),
    FOREIGN KEY (roleId) REFERENCES role(id)
);

-- 资源权限表
CREATE TABLE permission(
    id VARCHAR(32)  PRIMARY KEY,
    permissionName VARCHAR(50) ,
    url VARCHAR(50)
);

-- 角色权限关联表
CREATE TABLE role_permission(
    permissionId VARCHAR(32),
    roleId VARCHAR(32),
    PRIMARY KEY(permissionId,roleId),
    FOREIGN KEY (permissionId) REFERENCES permission(id),
    FOREIGN KEY (roleId) REFERENCES role(id)
);

2.2 创建实体类

创建Users

@Data
@NoArgsConstructor
public class Users{
    private int id;
    private String email;
    private String username;
    private String password;
    private String phoneNum;
    private int status;// '状态0 未开启 1 开启'
    private String statusStr;
    private Role role;

    public String getStatusStr() {
        if(status==0){
            statusStr="未开启";
        }else if(status==1){
            statusStr="开启";
        }
        return statusStr;
    }
}

创建Role

@Data
@NoArgsConstructor
public class Role {
    private int id;
    private String roleName;
    private String roleDesc;
    private List<Permission> permissions;
}

创建Permission

@Data
@NoArgsConstructor
public class Permission {
    private int id;
    private String permissionName;
    private String url;
}

2.3 Spring Security使用数据库认证

在Spring Security中如果想要使用数据进行认证操作,有很多种操作方式,这里我们介绍使用UserDetails、

UserDetailsService来完成操作。

  • UserDetails

    public interface UserDetails extends Serializable {
        Collection<? extends GrantedAuthority> getAuthorities();
    
        String getPassword();
    
        String getUsername();
    
        boolean isAccountNonExpired();
    
        boolean isAccountNonLocked();
    
        boolean isCredentialsNonExpired();
    
        boolean isEnabled();
    }
    

    UserDetails是一个接口,我们可以认为UserDetails作用是于封装当前进行认证的用户信息,但由于其是一个

    接口,所以我们可以对其进行实现,也可以使用Spring Security提供的一个UserDetails的实现类User来完成

    操作

    以下是User类的部分代码

    public class User implements UserDetails, CredentialsContainer {
        private String password;
        private final String username;
        private final Set<GrantedAuthority> authorities;
        private final boolean accountNonExpired; //帐户是否过期
        private final boolean accountNonLocked; //帐户是否锁定
        private final boolean credentialsNonExpired; //认证是否过期 
        private final boolean enabled; //帐户是否可用 
  • UserDetailsService

    public interface UserDetailsService {
        UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
    }
    
    

    上面将UserDetails与UserDetailsService做了一个简单的介绍,那么我们具体如何完成Spring Security的数据库认证操作哪,我们通过用户管理中用户登录来完成Spring Security的认证操作。

2.4 用户登录流程分析

在这里插入图片描述

2.5 代码编写

编写login和failer页面

在这里插入图片描述

导入spring security相关坐标

<!--spring-security-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>


配置web.xml

<!--加载spring环境 和 spring-security配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext*.xml,classpath:spring/spring-security.xml</param-value>
  </context-param>
 
<!-- 额外添加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-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">

    <!-- 配置不拦截的页面与静态资源 -->
    <security:http pattern="/login.jsp" security="none"/>
    <security:http pattern="/failer.jsp" security="none"/>
    <security:http pattern="/css/**" security="none"/>
    <security:http pattern="/img/**" security="none"/>
    <security:http pattern="/layui/**" security="none"/>

    <!--
    	配置具体的规则
    	auto-config="true"	不用自己编写登录的页面,框架提供默认登录页面
    	use-expressions="false"	是否使用SPEL表达式
    -->
    <security:http auto-config="true" use-expressions="false">
        <!-- 同源策略 如果页面使用iframe需要配置 否则不能使用 -->
        <security:headers>
            <security:frame-options disabled="true"/>
        </security:headers>
        <!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" -->
        <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />

        <!-- 定义跳转的具体的页面 -->
        <security:form-login
                login-page="/login.jsp"
                login-processing-url="/login"
                default-target-url="/index.jsp"
                authentication-failure-url="/failer.jsp"
                authentication-success-forward-url="/index.jsp"
        />

        <!-- 关闭跨域请求 -->
        <security:csrf disabled="true"/>

        <!-- 退出 -->
        <security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/login.jsp" />

    </security:http>

    <!-- 切换成数据库中的用户名和密码 -->
    <security:authentication-manager>
        <!-- user-service-ref="userService" springSecurity用于获取账号信息的类 需要实现UserDetailsService 重写加载数据方法  -->
        <security:authentication-provider user-service-ref="userService">
            <!-- 配置加密的方式 注意如果使用需要开启下面配置的加密类
            <security:password-encoder ref="passwordEncoder"/>-->
        </security:authentication-provider>
    </security:authentication-manager>


    <!-- 配置加密类 -->
    <!--<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>-->


    <!-- 提供了入门的方式,在内存中存入用户名和密码
    <security:authentication-manager>
    	<security:authentication-provider>
    		<security:user-service>
    			<security:user name="admin" password="{noop}admin" authorities="ROLE_USER"/>
    		</security:user-service>
    	</security:authentication-provider>
    </security:authentication-manager>
    -->

</beans>

spring security接收到用户名之后,spring security怎么就知道我们要调用的是那个service来完成用户的查询操作呢?

在配置文件中有这么一段配置:

<security:authentication-manager>
    <security:authentication-provider user-service-ref="userService">
        <!-- 配置加密的方式
            <security:password-encoder ref="passwordEncoder"/>-->
    </security:authentication-provider>
</security:authentication-manager>

user-service-ref属性就是来指定要执行的service,当然这个service来继承 UserDetailsService,扩展我们自己的service接口,完成用户的认证的操作

编写UserMapper

public interface UserMapper {
    //使用权限框架操作 根据账号查询信息
    @Select("select * from users where username=#{username} and status=1")
    public Users selectByUserName(String username);
    
}

编写UserService

public interface UserService extends UserDetailsService {
}

@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询数据返回实体类对应数据
        Users users = userMapper.selectByUserName(username);
        if(users!=null){
            //创建springSecurity主体中存储的账号对象并返回(账号,密码,权限列表)
            //权限列表这里填null注意
            UserDetails userDetails=new User(users.getUsername(),users.getPassword(),null);
            return userDetails;
        }
        return null;
    }

}

测试

在登录页面输入用户名 密码 跳转到了错误页面:

在这里插入图片描述

三、 用户登录问题分析

问题一:登录失败的问题

在数据库中是有jack这个用户的

在这里插入图片描述

为什么还是没办法登录,原因是在于我们的spring-security.xml配置文件中配置的问题:

在配置文件中,我们配置了

<!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" -->
<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>

spring security 拦截了所有的请求,要想登录认证必须是有ROLE_USER,ROLE_ADMIN两个权限的,在UserServiceImpl类中我将查询到的UserInfo中的信息封装到了User对象中,有一个值是为null

  User user = new User(userInfo.getUsername(),userInfo.getPassword(), null);

也就是说我们查询出的这个对象是没有权限的,既是用户名和密码对了,没有权限也是没有办法登录的!

在这里我们需要模拟给出ROLE_USER,ROLE_ADMIN两个权限,修改UserServiceImpl类

@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询数据返回实体类对应数据
        Users users = userMapper.selectByUserName(username);
        if(users!=null){
            //创建springSecurity主体中存储的账号对象并返回(账号,密码,权限列表)
            UserDetails userDetails=new User(users.getUsername(),users.getPassword(),getAuthority());
            return userDetails;
        }
        return null;
    }

    public List<SimpleGrantedAuthority> getAuthority(){
        ArrayList<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
        simpleGrantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        simpleGrantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        return  simpleGrantedAuthorities;
    }
}

修改完毕之后我们再次测试,看是否能登录成功!
在这里插入图片描述

这个报错的原因是spring security 默认使用的是密文提交的,现在没有进行加密! 我们只需要在获取密码的前面加上"{noop}" 代表的是使用明文

在这里插入图片描述

此时就认证成功

问题二:数据库查询用户角色

在上面的代码中我们的用户角色是自己手动添加的,这不是通用的一种方式,用户的角色我们要从数据库中进行查询!

修改UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.yanqi.ssm.mapper.UserMapper">

    <!--通过用户名查询用户-->
    <resultMap id="userResultMap" type="UserInfo" autoMapping="true">
        <id  property="id" column="id" />
        <collection property="roles" ofType="Role" javaType="List" autoMapping="true">
            <id  property="id" column="rid" />
        </collection>
    </resultMap>

    <select id="findUserByUserName"  resultMap="userResultMap">
        SELECT
	      * ,
          r.id rid
        FROM
             users  u,
             role   r,
             users_role ur
        WHERE
            u.id = ur.userId AND
            r.id = ur.roleId AND
            u.username = #{username}
    </select>

</mapper>

修改UserService

在这里需要注意的是,获取到对应的角色之后,其实还是不能登录,在users表中,有一个字段status,这个字段表示的用户是否可能,0表示可用 1表示不可用。此时的功能原来使用的构造已经无法满足了,我们需要使用另外一个构造

 public User(String username, String password,
                /**
                   boolean enabled: 账户是否可用
                   boolean accountNonExpired:账户是否过期
                   boolean credentialsNonExpired:密码是否过期
                   boolean accountNonLocked:账户是否冻结被锁定
                   authorities:该账户所具有的权限
                   
                */
             	boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, 
             		boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
 }

完整代码实现

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        UserInfo userInfo = userMapper.findUserByUserName(s);
        User user = new User(userInfo.getUsername(),
                "{noop}"+userInfo.getPassword(),
                 userInfo.getStatus() == 0 ? false : true ,
                true,true,true,
                 getAuthority(userInfo.getRoles()));

        return user;
    }

    public List<SimpleGrantedAuthority> getAuthority(List<Role> roles){
        ArrayList<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
        for (Role role : roles) {
            simpleGrantedAuthorities.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));
        }

        return  simpleGrantedAuthorities;
    }
}

四、动态权限

在实际开发中一般通过对数据库表的操作实现动态的权限操作,SpringSecurity实现动态权限比较复杂需要自己实现过滤器与决策器,所以这里的内容是基于权限管理crud功能实现后的配置,没有进行详细的讲解,如需详细了解可以查看Spring Security认证与授权的原理

4.1 将公开权限设置为无需认证即可访问

    <!-- 配置不拦截的资源 -->
    <security:http pattern="/login.jsp" security="none"/>
    <security:http pattern="/failer.jsp" security="none"/>
    <security:http pattern="/css/**" security="none"/>
    <security:http pattern="/img/**" security="none"/>
    <security:http pattern="/layui/**" security="none"/>

4.2 配置具体的规则

    <!--
    	配置具体的规则
    	auto-config="true"	不用自己编写登录的页面,框架提供默认登录页面
    	use-expressions="true"	是否使用SPEL表达式(否则只能使用USER_角色的形式配置)
    -->
    <security:http auto-config="true" use-expressions="true">
        <!-- 同源策略 如果页面使用iframe需要配置 否则不能使用 -->
        <security:headers>
            <security:frame-options disabled="true"/>
        </security:headers>

        <!-- 定义跳转的具体的页面 -->
        <security:form-login
                login-page="/login.jsp"
                login-processing-url="/login"
                default-target-url="/index.jsp"
                authentication-failure-url="/failer.jsp"
                authentication-success-forward-url="/users/name"
        />
        <security:intercept-url pattern="/**"   access="isAuthenticated()"/>
        <!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="isAuthenticated()" 
		<!-- 权限框架本质是过滤器链 会依次进行权限验证 如果验证通过继续执行  
		该配置 配置的是 除以上不拦截的资源外 所有url请求必须拥有认证的权限(登录后才能访问)
-->
        <!-- 关闭跨域请求 -->
        <security:csrf disabled="true"/>
        <!-- 退出 -->
        <security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/login.jsp" />
    </security:http>

在这里插入图片描述

2.3 设置权限数据

在这里插入图片描述

在这里插入图片描述

4.4 自定义相关过滤器与决策器

自定义决策管理器

权限框架本身是由多个不同功能的过滤器组成的,不同的过滤器负责不同的功能例如认证过滤、静态资源过滤、等 授权过滤也是一样,决策器就是同于判断当前请求的url当前账号是否拥有权限

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Iterator;
@Service
//决策管理器
public class MyAccessDecisionManager implements AccessDecisionManager {

    // decide 方法是判定是否拥有权限的决策方法,
    //authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
    //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
    //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

        if(null== configAttributes || configAttributes.size() <=0) {
            return;
        }
        ConfigAttribute c;
        String needRole;
        for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
            c = iter.next();
            needRole = c.getAttribute();
            for(GrantedAuthority ga : authentication.getAuthorities()) {//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
                if(needRole.trim().equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("no right");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

自定义权限加载器

import com.yunhe.javabean.Permission;
import com.yunhe.mapper.PermissionMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Service
//授权管理器
//用于当前项目要动态配置的权限信息
//从数据库中取出所有的权限信息 进行配置
//这样当客户请求对应权限时进行权限验证(因为不可能将所有的url都过滤)
public class MyInvocationSecurityMetadataSourceService  implements
        FilterInvocationSecurityMetadataSource {

    @Autowired
    //注入权限查询的dao层
    private PermissionMapper permissionMapper;

    private HashMap<String, Collection<ConfigAttribute>> map =null;

    /**
     * 加载权限表中所有权限
     */
    public void loadResourceDefine(){
        map = new HashMap<>();
        Collection<ConfigAttribute> array;
        ConfigAttribute cfg;
        //动态查询当前数据库中所有的权限
        List<Permission> permissions = permissionMapper.selectAll();
        for(Permission permission : permissions) {
            array = new ArrayList<>();
            cfg = new SecurityConfig(permission.getPermissionName());
            //此处只添加了权限的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
            array.add(cfg);
            //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
            //实际加载存储的结构为 url->[name1,name2....]
            //url是进行请求url拦截使用的  name是进行权限验证使用的
            //也就是说在用户进行授权时 实际加载的是权限名称

            map.put(permission.getUrl(), array);
        }

    }

    //此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        if(map ==null) loadResourceDefine();
        //object 中包含用户请求的request 信息
        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        AntPathRequestMatcher matcher;
        String resUrl;
        for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
            resUrl = iter.next();
            matcher = new AntPathRequestMatcher(resUrl);
            if(matcher.matches(request)) {
                return map.get(resUrl);
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

自定义权限拦截器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;

import javax.servlet.*;
import java.io.IOException;

@Service
//自定义权限拦截器
//FilterSecurityInterceptor是权限框架中用于处理权限验证的过滤器
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {


    //使用自己定义权限加载器
    @Autowired
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    //使用自定义的决策管理器
    @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }


    public void invoke(FilterInvocation fi) throws IOException, ServletException {
//fi里面有一个被拦截的url
//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
//执行下一个拦截器
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    @Override
    public void destroy() {

    }

    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }
}

4.5 将自定义权限拦截器配置入权限框架中

在security:http标签中添加

        <!-- 配置自定义拦截器 将定义拦截器配置到当前权限框架拦截器链中的指定位置
           将其配置在本身的权限认证过滤器之后执行
          -->
<security:custom-filter ref="myFilterSecurityInterceptor" after="FILTER_SECURITY_INTERCEPTOR"></security:custom-filter>

在这里插入图片描述

4.6 配置授权页面

在配置后使用账号登录会出现没有权限403代码页面

在这里插入图片描述

​ 如果用户登录进行操作时,直接报403错误用户体检并不好,我们可以制作一个比较好看的页面,告诉用户您用户不足,请联系管理员!

在web.xml中配置

  <error-page>
    <error-code>403</error-code>
    <location>/failer.jsp</location>
  </error-page>

4.7 授权

在书写权限加载器时,我们发现,加载器加载url是用于http请求的过滤匹配,而实际进行权限验证使用的是权限名称,为了方便书写,我们在登录认证时查询权限信息进行授权操作

修改PermissionMapper

    //根据用户id查询权限数据
    public List<Permission> selectByUid(int uid);
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.yunhe.mapper.PermissionMapper">
    <select id="selectByUid" resultType="com.yunhe.javabean.Permission">
        select p.*
        from users u,role r,permission p,users_role ur,role_permission rp
        where u.id=ur.userId and r.id=ur.roleId and r.id=rp.roleId and p.id =rp.permissionId and u.id=#{uid}
    </select>
</mapper>

修改PermissionService

    //根据uid查询权限数据
    public List<Permission> findByUid(int uid);
    @Override
    public List<Permission> findByUid(int uid) {
        return permissionMapper.selectByUid(uid);
    }

修改userService

将我们之前书写写死的角色认证与权限认证查询数据库的形式添加

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询数据返回实体类对应数据
        Users users = userMapper.selectByUserName(username);
        if (users != null) {
            //创建springSecurity主体中存储的账号对象并返回(账号,密码,权限列表)
            UserDetails userDetails = new User(users.getUsername(), users.getPassword(), getAuthority(users.getId()));
            return userDetails;
        }
        return null;
    }

    @Autowired
    PermissionService permissionService;
    //获取权限列表
    public List<GrantedAuthority> getAuthority(int uid) {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        //查询指定用户权限列表
        List<Permission> permissionList = permissionService.findByUid(uid);
        for (Permission p:permissionList ) {
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(p.getPermissionName());
            grantedAuthorities.add(grantedAuthority);
        }
        return grantedAuthorities;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用网络调试助手测试PLC-Reocrder收听模式的过程

目录 一、测试环境 二、步骤及要点说明 1、PLC-Recorder的通道配置 2、PLC-Recorder启动采集 3、配置网络调试助手 4、启动调试助手的连接&#xff0c;并点击“启动批量发送” 5、停止发送&#xff0c;查看发送和接收的情况 三、小结 一、测试环境 Windows10操作系统&a…

Docker进阶 - 11. Docker Compose 编排服务

注&#xff1a;本文只对一些重要步骤和yml文件进行一些讲解&#xff0c;其他的具体程序没有记录。 目录 1. 原始的微服务工程编排(不使用Compose) 2. 使用Compose编排微服务 2.1 编写 docker-compose.yml 文件 2.2 修改并构建微服务工程镜像 2.3 启动 docker-compose 服务…

数据结构(二):单向链表、双向链表

数据结构&#xff08;二&#xff09;一、什么是链表1.数组的缺点2.链表的优点3.链表的缺点4.链表和数组的区别二、封装单向链表1. append方法&#xff1a;向尾部插入节点2. toString方法&#xff1a;链表元素转字符串3. insert方法&#xff1a;在任意位置插入数据4.get获取某个…

RNN神经网络初探

目录1. 神经网络与未来智能2. 回顾数据维度和神经网络1. 神经网络与未来智能 2. 回顾数据维度和神经网络 循环神经网络&#xff0c;主要用来处理时序的数据&#xff0c;它对每个词的顺序是有要求的。 循环神经网络如何保存记忆功能&#xff1f; 当前样本只有 3 个特征&#x…

git基本概念图示【学习】

基本概念工作区&#xff08;Working Directory&#xff09;就是你在电脑里能看到的目录&#xff0c;比如名字为 gafish.github.com 的文件夹就是一个工作区本地版本库&#xff08;Local Repository&#xff09;工作区有一个隐藏目录 .git&#xff0c;这个不算工作区&#xff0c…

新方案:从错误中学习,点云分割中的自我规范化层次语义表示

前言 LiDAR 语义分割通过直接作用于传感器提供的原始内容来完成细粒度的场景理解而受到关注。最近的解决方案展示了如何使用不同的学习技术来提高模型的性能&#xff0c;而无需更改任何架构或数据集。遵循这一趋势&#xff0c;论文提出了一个从粗到精的设置&#xff0c;该设置从…

查找与排序 练习题

1、下列排序算法中&#xff0c;▁▁B▁▁ 是稳定的。 A.简单选择排序 B.冒泡排序 C.希尔排序 D.快速排序 解析&#xff1a;稳定排序是每次排序得到的结果是唯一的&#xff0c;不稳定排序得到的结果不唯一。 稳定&#xff1a;冒泡排序、归并排序、基数排序 不稳定&#x…

DolphinSchedule基于事件驱动的高性能并发编程

文章目录前言前置知识异步编程基于时间驱动的异步编程模式&#xff08;EAP Event-based Asynchronous Pattern &#xff09;实现EAPDolphinSchedule结合Netty实现Master与Worker之间的高性能处理能力的设计方案设计代码实现总结前言 研究DolphinSchedule的内因在于对调度系统并…

内存访问局部性特征

分享一道360的C语言笔试题。x是一个行列均为1000的二维数组&#xff0c;下面代码运行效率最高的是哪个&#xff1f; 二维数组大家都很熟悉&#xff0c;正常人遍历二维数组都是一行一行来的&#xff0c;为什么很少有人按列去遍历&#xff1f; 这道笔试题其实考察的就是遍历效率…

#车载基础软件——AUTOSAR AP技术形态

车载基础软件——AUTOSAR AP技术形态 我是穿拖鞋的汉子! 今天是2023年2月11日,时间好快,疫情解封已好几个月,生活节奏也在逐渐恢复到三年前的节奏。可能是感觉疫情与自己距离变远了,大家也开始慢慢的不再恐惧! 老规矩分享一段喜欢的文字,避免自己成为高知识低文化的工…

【安全】nginx反向代理+负载均衡上传webshell

目录 一、负载均衡反向代理下上传webshell Ⅰ、环境搭建 ①下载蚁剑&#xff0c;于github获取官方版&#xff1a; ②下载docker&docker-compose ③结合前面启动环境 ④验证 负载均衡下webshell上传 一、负载均衡反向代理下上传webshell 什么是反向代理&#xff1f; 通常的代…

大数据框架之Hadoop:入门(三)Hadoop运行环境搭建(开发重点)

3.1虚拟机环境准备 详见&#xff1a;yiluohan1234/vagrant_bigdata_cluster: 利用virtualbox快速搭建大数据测试环境 (github.com) 单纯只是安装虚拟机的话&#xff0c;注释掉40到115行。按照教程安装即可。 在 /opt 目录下创建 module、 software 文件夹 [roothdp101 ~]# m…

一、Java并发编程之线程、synchronized

黑马课程 文章目录1. Java线程1.1 创建和运行线程方法一&#xff1a;Thread方法二&#xff1a;Runnable&#xff08;推荐&#xff09;lambda精简Thread和runnable原理方法三&#xff1a;FutureTask配合Thread1.2 查看进程和线程的方法1.3 线程运行原理栈与栈帧线程上下文切换1.…

1. SpringMVC 简介

文章目录1. SpringMVC 概述2. SpringMVC 入门案例2.1 入门案例2.2 入门案例工作流程3. bean 加载控制4. PostMan 工具1. SpringMVC 概述 SpringMVC 与 Servlet 功能等同&#xff0c;均属于 Web 层开发技术。SpringMVC 是 Spring 框架的一部分。 对于 SpringMVC&#xff0c;主…

Python导入模块的3种方式

很多初学者经常遇到这样的问题&#xff0c;即自定义 Python 模板后&#xff0c;在其它文件中用 import&#xff08;或 from...import&#xff09; 语句引入该文件时&#xff0c;Python 解释器同时如下错误&#xff1a;ModuleNotFoundError: No module named 模块名意思是 Pytho…

45.在ROS中实现global planner(1)

前文move_base介绍&#xff08;4&#xff09;简单介绍move_base的全局路径规划配置&#xff0c;接下来我们自己实现一个全局的路径规划 1. move_base规划配置 ROS1的move_base可以配置选取不同的global planner和local planner&#xff0c; 默认move_base.cpp#L70中可以看到是…

Vue3电商项目实战-分类模块1【01-顶级类目-面包屑组件-初级、02-顶级类目-面包屑组件-高级】

文章目录01-顶级类目-面包屑组件-初级02-顶级类目-面包屑组件-高级01-顶级类目-面包屑组件-初级 目的&#xff1a; 封装一个简易的面包屑组件&#xff0c;适用于两级场景。 大致步骤&#xff1a; 准备静态的 xtx-bread.vue 组件定义 props 暴露 parentPath parentName 属性&am…

[oeasy]python0081_ANSI序列由来_终端机_VT100_DEC_VT选项_终端控制序列

更多颜色 回忆上次内容 上次 首先了解了RGB颜色设置可以把一些抽象的色彩名字 落实到具体的 RGB颜色 计算机所做的一切 其实就是量化、编码把生活的一切都进行数字化 标准 是ANSI制定的 这个ANSI 又是 怎么来的 呢&#xff1f;&#xff1f;&#x1f914; 由来 ANSI 听起…

【c++设计模式】——模板方法模式

模板方法模式的定义 定义一个操作中的算法对象的骨架&#xff08;稳定&#xff09;&#xff0c;而将一些步骤延迟到子类&#xff08;定义一个虚函数&#xff0c;让子类去实现&#xff09;&#xff0c;template method使得子类可以不改变&#xff08;复用&#xff09;一个算法结…

can协议介绍

目录 1 can协议介绍 1.1can协议 1.2 CAN协议特点 2.CAN FD 2.1 CAN FD协议简介 2.2 CAN FD协议特点 3.LIN 3.1 LIN总线简介 3.2 LIN总线特点 4. FlexRay 4.1 FlexRay简介 4.2 FlexRay特点 5. MOST 6.Ethernet 7 总结&#xff1a; 1 can协议介绍 1.1can协议 CAN…