Spring学习笔记之Spring Security

1.简介

Spring Security是为基于spring的应用程序提供声明式安全保护的安全性框架。它能够在Web请求级别和方法调用级别处理身份认证和授权。

1.1Spring Security模块

Spring Security被分成11个模块

模块 描述
ACL 支持通过访问控制列表(access control list,ACL)为域对象提供安全性
切面(Aspects) 一个很小的模块,当使用Spring Security注解时,会使用基于AspectJ的切面,而不是使用标准的Spring AOP
CAS客户端(CAS Client) 提供Jasig的中心认证服务(Central Authentication Service, CAS)进行集成的功能
配置(Configuration) 包含通过XML和Java配置Spring Security的功能支持
核心(core) 提供Spring Security基本库
加密(Cryptography) 提供加密和密码编码的功能
LDAP 支持基于LDAP进行认证
OpenID 支持使用OpenID进行集中式认证
Remoting 提供了对Spring Remoting的支持
标签库(Tag Library) Spring Security的JSP标签库
Web 提供了Spring Security基于Filter的Web安全性支持

应用程序的类路径下至少要包含Core和Configuration这两个模块。

1.2 过滤Web请求

使用Spring Security,只需要在Servlet上下文配置一个Filter——DelegatingFilterProxy就可以,这是一个特殊的Servlet Filter,它本身所做的工作并不多。只是将工作委托给一个javax.servlet.Filter实现类,这个实现类作为一个注册在Spring应用上下文中。下面是两种配置方式:
XML:在web.xml中

<filter>
    <filter-name>springSecurityFilterChainfilter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
  filter>

Java配置:创建一个扩展的新类


import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer{

}

1.3简单的配置

Java配置:
任何实现了WebSecurityConfigure的类都可以用来配置Spring Security。更简单的方式是扩展WebSecurityConfigure的一个实现类WebSecurityConfigurerAdapter。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{}

如果你的应用是使用Spring MVC开发的,可以使用@EnableWebMvcSecurity来替换:

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{}

具体的配置可以通过重写WebSecurityConfigurerAdapter的configure()方法来实现,下面是三个方法的功能:

方法 描述
configure(HttpSecurity) 通过重载,配置如何通过拦截器保护请求
configure(WebSecurity) 通过重载,配置Spring Security的Filter链
configure(AuthenticationManagerBuilder) 通过重载,配置user-detail服务

下面是几个示例:
1.配置用户存储,这里使用基于内存的用户存储,此外,它还支持基于数据库表认证、基于LDAP进行认证以及自定义的用户服务(提供一个自定义的UserDetailsService接口实现。)

/**
     * 配置用户存储
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //使用内存配置用户存储
        auth.inMemoryAuthentication()
            .withUser("xuexiaoqiang").password("123").roles("USER").and()
            .withUser("admin").password("admin").roles("USER","ADMIN");//添加了两个用户并配置了用户角色权限

        //使用以JDBC为支撑的用户存储
//      auth.jdbcAuthentication()
//          .dataSource(dataSource)
//          .usersByUsernameQuery(
//                  "select username, password, true " + 
//                  "from Spitter where username=?")//重写查询语句
//          .authoritiesByUsernameQuery(
//                  "select username, 'ROLE_USER' from Spitter where username=?")
//          .passwordEncoder(new StandardPasswordEncoder("53cr3t"));//指定密码转换器

    }

2.指定哪些请求需要认证,哪些请求不需要认证,以及所需要的权限:

/**
     * 请求控制
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/me").authenticated() //对路径“/me”的请求需要进行认证
                .antMatchers(HttpMethod.GET, "/index").hasAuthority("ROLE_ADMIN") //对路径“/index”下的get请求需要ADMIN权限
                .anyRequest().permitAll()//其他的所有请求不需要认证
            .and()
            .requiresChannel()
                .regexMatchers("/form").requiresSecure()//只要是对"/form"的请求,Spring Security都视为需要安全通道并自动将请求重定向到HTTPS上。
                .antMatchers("/").requiresInsecure();//对“/”的访问用HTTP通道
        //http.csrf().disable();//禁用CSRF防护功能

        //启用默认的登录页
        http.formLogin();

        //启用remember me功能
        http.rememberMe()
                .tokenValiditySeconds(2419200)//指定token生命周期
                .key("testKey");//私钥

        //退出后重定向到其他页面,默认是返回登录页
        http.logout()
                .logoutSuccessUrl("/")
                .logoutUrl("/signout");//重写LogoutFilter拦截路径,默认为/logout
    }
    //这里的ant指的是ant风格的通配符,regexMatchers()则是正则风格的通配符

除了authenticated() 和permitAll(),AuthorizedUrl还有下面的方法用来定义如何保护路径的配置方法

方法 描述
not() 对其他访问方法的结果求反
hasRole(String role) 如果用户具备给定角色的话,就允许访问
hasAnyRole(String… roles) 如果用户具备给定角色中的某一个的话,就允许访问
hasAuthority(String authority) 如果用户具备给定权限的话,就允许访问
hasAnyAuthority(String… authorities) 如果用户具备给定权限中的某一个的话,就允许访问
hasIpAddress(String ipaddressExpression) 如果请求来自给定IP的话,就允许访问
permitAll() 无条件允许访问
anonymous() 允许匿名用户访问
rememberMe() 如果用户是通过Remember-me功能认证的,就允许访问
denyAll() 无条件拒绝所有访问
authenticated() 允许认证过的用户访问
fullyAuthenticated() 如果用户是完整认证的话(不是通过Remember-me功能认证的),就允许访问
access(String attribute) 如果给定的spEl表达式计算结果为true,就允许访问

配置的规则是将最为具体的请求路径放在前面,而最不具体的路径(如antRequest())放在最后面。否则的话,不具体的路径配置将会覆盖掉更为具体的路径配置。

上面提到了access方法,它可以使用spEL表达式进行访问控制
Spring Security通过一些安全性相关的表达式扩展了Spring表达式语言

安全表达式 计算结果
authentication 用户的认证对象
denyAll 结果始终未false
hasAnyRole(list of roles) 如果用户被授予了列表中任意的指定角色,结果为true
hasRole(role) 如果用户被授予了指定的角色,结果为true
hasIPAddress(IP Address) 如果请求来自指定IP的话,结果为true
isAnonymous() 如果当前用户为匿名用户,结果为true
isAuthenticated() 如果当前用户进行了认证的话,结果为true
isFullyAuthenticated() 如果当前用户进行了完整认证的话(不是通过Remember-me功能进行的认证),结果为true
isRememberMe() 如果当前用户是通过Remember-me自动认证的,结果为true
permitAll 结果始终为true
principal 用户的principal对象

spELl的特点就是可以对各种控制进行自由的搭配,比如下面这个

http.authorizeRequests()
                .regexMatchers("/*")
                .access("hasRole('ROLE_ADMIN') and hasIpAddress('192.168.1.2')");

强制通道:
使用requiresChannel()方法能够为各种URL模式声明所要求的通道。

防止跨站请求伪造——CSRF:
Spring Security通过一个同步token的方式来实现CSRF防护的功能。
这意味着在应用中所有的表单必须在一个“_csrf”域中提交token,而且这个token必须要与服务器端计算并存储的token一致,这样的话当表单提交的时候,才能进行匹配。
下面是使用不同的模板时使用csrf的方式:
Thymeleaf:在action属性添加Thymeleaf命名空间前缀,下面的th:,就会自动生成一个“_csrf”隐藏域。

<form method="POST" th:action="@{/websecurity}">

JSP:

type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />

如果使用Spring的表单绑定标签的话标签会自动为我们添加隐藏的CSRF token标签。
下面是Spring Security默认的登录页面,里面就有“_csrf”隐藏域以及token。

<html>
<head>
<title>Login Pagetitle>
head>
<body onload='document.f.username.focus();'>
    <h3>Login with Username and Passwordh3>
    <form name='f' action='/websecurity/login' method='POST'>
        <table>
            <tr>
                <td>User:td>
                <td><input type='text' name='username' value=''>td>
            tr>
            <tr>
                <td>Password:td>
                <td><input type='password' name='password' />td>
            tr>
            <tr>
                <td><input type='checkbox' name='remember-me' />td>
                <td>Remember me on this computer.td>
            tr>
            <tr>
                <td colspan='2'><input name="submit" type="submit"
                    value="Login" />td>
            tr>
            <input name="_csrf" type="hidden"
                value="bdd0204e-c2d4-45b9-a11c-6e6b080d243c" />
        table>
    form>
body>
html>

Remember-me:
默认情况下,这个功能是通过在cookie中存储一个token完成的,这个token最多两周有效。
存储在cookie中的token包含用户名,密码,过期时间,和一个私钥——在写入cookie前都进行了MD5哈希。
启用了Remember-me功能后,我们只需要在登录表单中增加这样一个复选框就可以了,不用写value值,如果被选中的话,会发送一个“on”过去:

type="checkbox" name="remember-me" id="remember-me" />

这是那个cookie:
Spring学习笔记之Spring Security_第1张图片

2.保护视图内容

Spring Security本身提供了一个JSP标签库,而Thymeleaf通过特定的方言实现了与Spring Security的集成。

2.1 Spring Security的JSP标签库

声明:

<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security"%>

使用这个标签库要记得加依赖:

<dependency>
        <groupId>org.springframework.securitygroupId>
        <artifactId>spring-security-taglibsartifactId>
        <version>4.2.2.RELEASEversion>
    dependency>
JSP标签 作用
如果用户通过访问控制列表授予了指定的权限,那么渲染该标签体重的内容
渲染当前用户认证对象的详细信息
如果用户被授予了特定的权限或者SpEL表达式的计算结果为true,那么渲染该标签体中的内容

例如:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
head>
<body>
    <h1>My Name is <security:authentication property="principal.username"/>h1>
    
    <security:authentication property="principal.password" var="password" scope="request"/>

    
    <security:authorize access="hasRole('ROLE_ADMIN')">
        <h1>哈哈哈,这里只有具有ADMIN权限的用户才能看到h1>
    security:authorize>
body>
html>

使用JSP标签来访问用户的认证详情

认证属性 描述
authorities 一组用于表示用户所授予权限的GrantedAuthority对象
Credentials 用于核实用户的凭证(通常,这会是用户的密码)
details 认证的附加信息(IP地址,证件序列号、会话ID等)
principal 用户的基本信息对象

2.2使用Thymeleaf的Spring Security方言

Thymeleaf的安全方言提供了与Spring Security标签库相对应的属性:

属性 作用
security:authentication 渲染认证对象的属性。类似于Spring Security 的
security:authorize 基于表达式的计算结果,条件性的渲染内容。类似于Spring Security的
security:authorize-acl 基于表达式的计算结果,条件下的渲染内容。类似于
security:authorize-expr security:auhorize属性的别名
security:authorize-url 基于给定URL路径相关的安全规则,条件性的渲染内容。类似于标签使用url属性时的场景。

为了使用安全方言,需要确保Thymeleaf Extras Spring Security已经位于应用的类路径下。然后,还需要在配置中使用SpringTemplateEngine来注册SpringSecurityDialect:

"templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
      ...
      "additionalDialects">
        
          
          "org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect"/>
        
      
      ...
    

然后再模板中声明命名空间:

"http://www.w3.org/1999/xhtml"
        xmlns:th="http://www.thymeleaf.org"
        xmlns:security="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
...

然后就可以使用了:

<div th:text="${#authentication.name}">
        The value of the "name" property of the authentication object should appear here.
    div>
<div th:if="${#authorization.expression('hasRole(''ROLE_ADMIN'')')}">
        This will only be displayed if authenticated user has role ROLE_ADMIN.
    div>

你可能感兴趣的:(Spring学习笔记)