Spring Security 学习笔记

Spring Security

1、认证和授权

关于认证和授权,查资料是这样解释的

认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。

授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。

先认证后授权

2、权限模块数据模型

要实现最终的权限控制,需要有一套数据模型来做支撑,一般的表结构如下:

用户表——权限表——角色表——菜单表——用户角色关系表——角色权限关系表——角色菜单关系表

类似上面这种表结构

认证的过程:只需要用户表,在登录时查询用户信息,判断用户名和密码是否正确。这一过程较为简单

授权的过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色,再根据角色查询对应的菜单,这样就确定了用户能够看到哪些菜单。然后再根据用户的角色查询对应的权限,这样就确定了用户拥有哪些权限。所以授权过程会用到上面7张表。

3、Spring Security

SS是spring提供的安全认证服务框架。使用ss可以帮助我们简化认证和授权的过程。

这是他的官网:https://docs.spring.io/spring-security/site/docs/5.4.5/reference/html5/

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>${spring.security.version}</version>
</dependency>
<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${spring.security.version}</version>
</dependency>

3.1、工程搭建

创建maven工程,打包方式为war包,把需要的依赖文件添加到pom.xml文件中。

如果需要tomcat插件,需进行以下配置:

<build>
       <plugins>
           <plugin>
               <groupId>org.apache.tomcat.maven</groupId>
               <artifactId>tomcat7-maven-plugin</artifactId>
               <configuration>
                   <!-- 指定端口 -->
                   <port>85</port>
                   <!-- 请求路径 -->
                   <path>/</path>
               </configuration>
           </plugin>
       </plugins>
   </build>

提供index.html文件,提供内容Hello Spring Security!

3.2、配置web.xml

在web.xml中配置SpringMVC中的DisPatcherServlet和用于整合第三方框架的DelegatingFilterProxy,用于整合spring Security。

<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <filter>
   <!--
     DelegatingFilterProxy用于整合第三方框架
     整合Spring Security时过滤器的名称必须为springSecurityFilterChain,
 否则会抛出NoSuchBeanDefinitionException异常
   -->
   <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>
 <servlet>
   <servlet-name>springmvc</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
   <init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>classpath:spring-security.xml</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
   <servlet-name>springmvc</servlet-name>
   <url-pattern>*.do</url-pattern>
 </servlet-mapping>
</web-app>

注意:DelegatingFilterProxy用于整合第三方框架 整合Spring Security时过滤器的名称必须为springSecurityFilterChain,否则会抛出NoSuchBeanDefinitionException异常

3.3 配置 spring-security.xml

在spring-security.xml中主要配置认证规则和拦截管理器。

<?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:context="http://www.springframework.org/schema/context"
      xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:security="http://www.springframework.org/schema/security"
      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://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
                    http://www.springframework.org/schema/security
                    http://www.springframework.org/schema/security/spring-security.xsd">
   <!--
       auto-config:自动配置,如果设置为true,表示自动应用一些默认配置,比如框架会提供一个默认的登录页面
       use-expressions:是否使用spring security提供的表达式来描述权限
   -->
   <security:http auto-config="true" use-expressions="true">
<!--
intercept-url pattern="/**":所有的资源
/* 是拦截所有的文件夹,不包含子文件夹
/** 是拦截所有的文件夹及里面的子文件夹
access="hasRole('ROLE_ADMIN')":访问该资源所需要的的权限
-->
       <security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"></security:intercept-url>
   </security:http>
   <security:authentication-manager>
       <security:authentication-provider>
           <security:user-service>
<!--
{noop}1234:明文密码1234
-->
               <security:user name="admin" authorities="ROLE_ADMIN" password="{noop}1234"/>
           </security:user-service>
       </security:authentication-provider>
   </security:authentication-manager>

</beans>

3.3 运行测试

访问localhost:85/index.html

因为没有权限,所以跳转到spring security自带的登录页面。

进行登录后,重新访问

访问成功了!

4、对入门案例进行改进

前面我们已经完成了Spring Security的入门案例,通过入门案例我们可以看到,Spring Security将我们项目中的所有资源都保护了起来,要访问这些资源必须要完成认证而且需 要具有ROLE_ADMIN角色。

但是入门案例中的使用方法离我们真实生产环境还差很远,还存在如下一些问题:

1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源 不需要认证也可以访问,也就是可以匿名访问。 access="hasRole('ROLE_ADMIN')" />

2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。

3、直接将用户名和密码配置在了配置文件中,而真实生产环境下的用户名和密码往往保存在数据库中。

4、在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行 加密。

4.1配置可匿名访问的资源

因为上面的配置中“/**”代表对所有的资源都进行权限的判断,但实际生产环境中,我们并不需要将所有的页面都进行权限配置比如登录页面。

所以我们需要进行下面的配置:

    <security:http security="none" pattern="/pages/a.html"></security:http>
   <security:http security="none" pattern="/pages/b.html"></security:http>

然后再运行测试,发现a.html和b.html可以访问,之前的index.html任然需要登录才可以进入。

4.2 配置自己自定义的登录页面

准备工作:一个自定义的登录页面,放入相应的目录中

  1. 配置该页面可匿名访问

<security:http security="none" pattern="/pages/login.html"></security:http>
  1. 如果我们使用自己的登录页面,必须配置登录表单,页面提交的登录请求由框架处理

<security:form-login
               login-page="/pages/login.html"
               username-parameter="username"
               password-parameter="password"
               login-processing-url="/login.do"
               default-target-url="/index.html"
               authentication-failure-url="/pages/login.html"
></security:form-login>

login-page="/pages/login.html":表单页面

username-parameter="username" password-parameter="password" :相对应的用户名和密码

default-target-url="/index.html" authentication-failure-url="/pages/login.html" :登录成功和失败所对应的页面

  1. 关闭CsrfFilter过滤器,默认开启

<!‐‐
csrf:对应CsrfFilter过滤器
disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则
登录操作会被禁用(403)
‐‐>
<security:csrf disabled="true"></security:csrf>

为什么关闭csrs过滤器,因为在使用默认的登录界面,会有一个隐藏域生成一个随机值,框架进行对比,如果不一致,会认为这是伪造的认证请求,登录操作会被禁用。

4.3从数据库查询信息(首先通过map模拟数据库)

如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个 实现UserService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。

实现代码:

先通过map集合模拟数据库查询

public class SpringSercuity implements UserDetailsService {

   public static Map<String,User> map=new HashMap<>();
   static {
       User user=new User();
       user.setUsername("xiaohong");
       user.setPassword("123456");
       User user1=new User();
       user1.setUsername("xiaoming");
       user1.setPassword("123456");
       map.put(user.getUsername(),user);
       map.put(user1.getUsername(),user1);
  }
   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       //System.out.println(username);
       User user=map.get(username);
       //System.out.println(user.toString());
       if (user == null){
           return null;
      }else {
           //当前登录用户授权,后期改为数据库查询当前对应的权限
           List<GrantedAuthority> list =new ArrayList<>();
           list.add(new SimpleGrantedAuthority("permission_A")); //授予权限
           list.add(new SimpleGrantedAuthority("permission_A"));
           list.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //授予角色
           org.springframework.security.core.userdetails.User SecurityUser=new org.springframework.security.core.userdetails.User(username,"{noop}"+user.getPassword(),list);
           return SecurityUser;
      }

  }
}

4.4 对密码进行加密

前面我们使用的密码都是明文的,这是非常不安全的。一般情况下用户的密码需要进行 加密后再保存到数据库中。

常见的密码加密方式有: 3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码

MD5、SHA1:使用单向HASH算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破解

bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而 无需单独处理salt问题 加密后的格式一般为:

$2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa

加密后字符串的长度为固定的60位。其中:$是分割符,无意义;2a是bcrypt加密版本 号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了。

实现步骤:

  1. 配置文件中添加相关的加密对象的bean

<!--    配置密码加密对象-->
   <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
  1. 自动注入

@Autowired
   private BCryptPasswordEncoder passwordEncoder;
  1. 调用自带方法,实现密码加密

User user=new User();
       user.setUsername("xiaohong");
       //使用BCrypt对密码进行加密
       user.setPassword(passwordEncoder.encode("admin"));
       User user1=new User();
       user1.setUsername("xiaoming");
       user1.setPassword(passwordEncoder.encode("admin"));

4.5 配置多种校验规则

为了测试方便,首先在项目中创建a.html、b.html、c.html、d.html几个页面 修改spring-security.xml文件:

<!‐‐只要认证通过就可以访问‐‐>
<security:intercept‐url pattern="/index.jsp" access="isAuthenticated()"/>
<security:intercept‐url pattern="/a.html" access="isAuthenticated()" />
<!‐‐拥有add权限就可以访问b.html页面‐‐>
<security:intercept‐url pattern="/b.html" access="hasAuthority('add')"
/>
<!‐‐拥有ROLE_ADMIN角色就可以访问c.html页面‐‐>
<security:intercept‐url pattern="/c.html" access="hasRole('ROLE_ADMIN')"
/>
<!‐‐拥有ROLE_ADMIN角色就可以访问d.html页面,
注意:此处虽然写的是ADMIN角色,框架会自动加上前缀ROLE_‐‐>
<security:intercept‐url pattern="/d.html" access="hasRole('ADMIN')" />

记得把上面案例用到的校验规则移除

access="hasRole('ROLE_ADMIN')==access="hasRole('ADMIN')":框架会自动加入ROLE_

4.6 注解方式权限控制

Spring Security除了可以在配置文件中配置权限校验规则,还可以使用注解方式控制类 中方法的调用。

例如Controller中的某个方法要求必须具有某个权限才可以访问,此时就可以使用Spring Security框架提供的注解方式进行控制。

实现步骤:

第一步:在spring-security.xml文件中配置组件扫描,用于扫描Controller

<mvc:annotation‐driven></mvc:annotation‐driven> <context:component‐scan base‐package="com.itheima.controller"> </context:component‐scan>

第二步:在spring-security.xml文件中开启权限注解支持

<!‐‐开启注解方式权限控制‐‐> <security:global‐method‐security pre‐post‐annotations="enabled" />

第三步:创建Controller类并在Controller的方法上加入注解进行权限控制

@RestController 
@RequestMapping("/hello") public class HelloController {
   @RequestMapping("/add")
   @PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
   public String add(){
       System.out.println("add...");
       return "success";
  }
   @RequestMapping("/delete")
   @PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色 才能调用当前方法
   public String delete(){
       System.out.println("delete...");
       return "success";
  }
}

4.7 退出登录

用户完成登录后Spring Security框架会记录当前用户认证状态为已认证状态,即表示用 户登录成功了。那用户如何退出登录呢?

我们可以在spring-security.xml文件中进行如下配置:

<!‐‐logout:退出登录 
logout‐url:退出登录操作对应的请求路径 logout‐success‐url:
退出登录后的跳转页面 ‐‐>
<security:logout
                logout‐url="/logout.do"
                logout‐success‐url="/login.html"
                invalidate‐ session="true"/>

通过上面的配置可以发现,如果用户要退出登录,只需要请求/logout.do这个URL地址就 可以,同时会将当前session失效,最后页面会跳转到login.html页面。

posted @ 2021-03-02 00:22  吃饼干的南博one  阅读(86)  评论(0)    收藏  举报