Java-Security(一):初体验

Spring Security的前身是Acegi,在被收纳为Spring子项目后正式更改名为 Spring Security。本博客基于Spring Security的版本是 5.2.0.RELEASE。

Spring Security 5.2.0.RELEASE 支持原声的OAuth2框架,支持更现在化的密码加密方式。

Spring Security支持广泛的认证技术,这些技术大多由三方或相关标准组织开发。Spring Security已经集成的认证技术支持如下:

  •     HTTP BASIC authentication headers (一个基于IETF RFC的标准)
  •     HTTP Digest authentication headers (一个基于IETF RFC的标准)
  •     HTTP X.509 client certificate exchange (一个基于IETF RFC的标准)
  •     LDAP (一个非常常见的跨平台认证需要做法,特别是在大环境)
  •     Form-based authentication (提供简单用户接口的需求)
  •     OpenID authentication(一种去中心化的身份认证方式)
  •     Authentication based on pre-established request headers(基于预先建立的请求头进行认证):(比如Computer Associates Siteminder,一种用户身份验证及授权的集中式安全基础方案)
  •     JA-SIG Central Authentication Service (也被称为CAS,这是一个流行的开源单点登录系统)
  •     Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker (一个Spring远程调用协议)
  •     Automatic “remember-me” authentication (这样你可以设置一段时间,避免在一段时间内还需要重新验证)
  •     Anonymous authentication (允许未认证的任何调用,自动假设一个特定的安全主体)
  •     Run-as authentication (这在一个会话内使用不同安全身份的时候是非常有用的)
  •     Java Authentication and Authorization Service (JAAS,java验证和授权API)
  •     Java EE container authentication:允许系统继续使用容器管理这种身份验证方式
  •     Kerberos:一种使用对称秘钥机制,允许客户端与服务器相互确认身份和认证协议。

除此之外,Spring Security还引入了一些第三方包,用于支持更多认证技术,比如:JOSSO等。如果这些技术都无法满足需求,则Spring Security允许我们编程写自己的认证技术。

因此,在大部分情况下,当我们有java应用安全方面需求时,选择Spring Security往往是正确的而有效的。

在授权上,Spring Security 不仅支持基于URL对Web的请求授权,还支持方法访问授权、对象访问授权等,基本涵盖了常见的大部分授权场景。

新建Spring Security Maven项目

1)新建spring-security-01 maven 项目

pom.xml如下:

<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>
    <springframework.version>5.2.0.RELEASE</springframework.version>
    <com.alibaba.version>1.1.21</com.alibaba.version>
    <mysql.version>8.0.11</mysql.version>
    <org.mybatis.version>3.4.6</org.mybatis.version>
    <org.mybatis.spring.version>2.0.3</org.mybatis.spring.version>
    <org.aspectj.version>1.9.4</org.aspectj.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${springframework.version}</version>
    </dependency>

    <!--AOP aspectjweaver 支持 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>${org.aspectj.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${org.aspectj.version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-core</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>${springframework.version}</version>
    </dependency>

    <!--访问RDBMS-MySQL依赖 -->
    <!--MyBatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${org.mybatis.version}</version>
    </dependency>
    <!-- Mybatis自身实现的Spring整合依赖 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>${org.mybatis.spring.version}</version>
    </dependency>

    <!--MySql数据库驱动 -->
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${com.alibaba.version}</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.13</version>
    </dependency>

    <!--form 设置为enctype="multipart/form-data",多文件上传,在applicationContext.xml中配置了bean
            multipartResolver时,需要依赖该包。 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.5</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>

    <!-- 编译依赖 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!--日志支持 -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.26</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.26</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

    <!-- redis依赖包 -->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>2.2.3.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
View Code

pom中引入部分包含:spring-framework,springmvc,spring-security,其他。

2)编写web.xml等配置文件

2.1)web.xml配置

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <welcome-file-list>
        <welcome-file>/index</welcome-file>
    </welcome-file-list>

    <!--加载dao/service/一些共享组件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:applicationContext-base.xml,
            classpath:applicationContext-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>

    <filter>
        <filter-name>multipartFilter</filter-name>
        <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
        <init-param>
            <param-name>multipartResolverBeanName</param-name>
            <param-value>multipartResolver</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>multipartFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
        <init-param>
            <param-name>methodParam</param-name>
            <param-value>_method</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

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

    <!--加载springmvc controller viewsolver 等-->
    <servlet>
        <servlet-name>spring-security-01</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-security-01-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-security-01</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
View Code

web.xml中需要引入filter( characterEncodingFilter 、hiddenHttpMethodFilter 、multipartFilter 、 springSecurityFilterChain):
    2.1.1)characterEncodingFilter:用来实现对请求内容进行encoding处理,防止乱码;
    2.1.2)hiddenHttpMethodFilter:用来在表单中添加hidden标签,value=put|delete,实现put、delete方式提交表单;
    2.1.3)multipartFilter:上传文件过滤器;
    2.1.4)springSecurityFilterChain:加入spring security拦截器,实现认证、授权拦截。

2.2)监听器ContextLoaderListener

用来初始化spring applicationContext,用来自动扫描dao、service组件,上创解析器组件,以及spring security组件集成;
applicationContext-base.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.dx.test.security"/>

    <!--上传文件解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="utf-8"></property>
        <property name="maxUploadSize" value="10240000"></property>
    </bean>
</beans>

applicationContext-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: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/security https://www.springframework.org/schema/security/spring-security.xsd">
    <!--
    从Spring Security 3.1开始,可以使用多个http元素为不同的请求模式定义单独的安全过滤器链配置。
    -->
    <security:http pattern="/css/**" security="none"/>

    <security:http auto-config="true" use-expressions="false">
        <security:csrf disabled="false"/>
        <security:intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <security:intercept-url pattern="/index" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <security:intercept-url pattern="/**" access="ROLE_USER"/>

        <security:form-login default-target-url="/index" />
        <security:logout delete-cookies="JSESSIONID" logout-success-url="/login" logout-url="/logout" />

    </security:http>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that
                NoOpPasswordEncoder should be used. This is not safe for production, but makes reading
                in samples easier. Normally passwords should be hashed using BCrypt
                -->
                <security:user name="admin" password="{noop}adminpwd" authorities="ROLE_USER, ROLE_ADMIN"/>
                <security:user name="user" password="{noop}userpwd" authorities="ROLE_USER"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>

</beans>

或者 去掉security:前缀,将<security:http/> 简写为<http>的另外一种配置方式:

<?xml version="1.0" encoding="UTF-8"?>
<bean:beans xmlns:bean="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="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/security https://www.springframework.org/schema/security/spring-security.xsd">
    <http pattern="/css/**" security="none"/>

    <http auto-config="true" use-expressions="false">
        <csrf disabled="false"/>
        <intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <intercept-url pattern="/index" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <intercept-url pattern="/**" access="ROLE_USER"/>
        <form-login default-target-url="/index" />
        <logout delete-cookies="JSESSIONID" logout-success-url="/login" logout-url="/logout" />
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" password="{noop}adminpwd" authorities="ROLE_USER, ROLE_ADMIN"/>
                <user name="user" password="{noop}userpwd" authorities="ROLE_USER"/>
            </user-service>
        </authentication-provider>
    </authentication-manager>
</bean:beans>

2.2.1)从Spring Security 3.1开始,可以使用多个http元素为不同的请求模式定义单独的安全过滤器链配置,详情从上边配置可见。
2.2.2)form-login是spring security命名空间配置登录相关信息的标签,它包含如下属性:
    1. login-page 自定义登录页url,默认为/login
    2. login-processing-url 登录请求拦截的url,也就是form表单提交时指定的action
    3. default-target-url 默认登录成功后跳转的url
    4. always-use-default-target 是否总是使用默认的登录成功后跳转url
    5. authentication-failure-url 登录失败后跳转的url
    6. username-parameter 用户名的请求字段 默认为userName
    7. password-parameter 密码的请求字段 默认为password
    8. authentication-success-handler-ref 指向一个AuthenticationSuccessHandler用于处理认证成功的请求,不能和default-target-url还有always-use-default-target同时使用
    9. authentication-success-forward-url 用于authentication-failure-handler-ref
    10. authentication-failure-handler-ref 指向一个AuthenticationFailureHandler用于处理失败的认证请求
    11. authentication-failure-forward-url 用于authentication-failure-handler-ref
    12. authentication-details-source-ref 指向一个AuthenticationDetailsSource,在认证过滤器中使用
2.2.3)密码的前缀是{noop},以指示DelegatingPasswordEncoder应使用NoOpPasswordEncoder。这对生产是不安全的,但会使在demo中阅读更容易。通常密码应该使用BCrypt散列。

2.3)sevlet实现类DispatcherServlet

用来初始化springmvc系统,指定servlet实现类,加载springmvc的controller,view视图等;
加载文件spring-security-01-servlet.xml中指定了:自动扫描controller包路径、自动扫描驱动包、开启aop、jsp视图解析器。

<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 开启controller注解支持 -->
    <context:component-scan base-package="com.dx.test.controller" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <!-- 使用注解驱动:自动配置处理器映射器与处理器适配器 -->
    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- 开启aop,对类代理 -->
    <aop:config proxy-target-class="true"></aop:config>

    <!-- 单独使用jsp视图解析器时,可以取消掉注释,同时注释掉:下边的‘配置多个视图解析’配置-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

 添加views页面、controller控制类、测试项目

1)添加views页面和controller类

1)新建view页面webapp/WEB-INF/views/index.jsp

<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

2)新建LoginController.java

package com.dx.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class LoginController {
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String loginPage(@RequestParam(value = "error", required = false) String error, Model model) {
        if (error != null) {
            return "login-failure";
        }
        return "login";
    }
}

3)新建IndexController.java

package com.dx.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class IndexController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home() {
        return "redirect:/index";
    }

    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index() {
        return "/index";
    }
}

2)启动项目测试

启动项目,自动跳转到http://localhost:8080/spring_security_01_war/下,此时允许访问,输入地址:http://localhost:8080/spring_security_01_war/index也是允许访问的,因为我们再applicationContext-security.xml中配置了'/'和‘/index’是允许任何人访问的,因此这里可以访问‘http://localhost:8080/spring_security_01_war/’和‘http://localhost:8080/spring_security_01_war/index’。

访问'http://localhost:8080/spring_security_01_war/admin',此时会自动跳转到‘http://localhost:8080/spring_security_01_war/login’登录页面。

从页面查看login页面html代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Please sign in</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
    <link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
  </head>
  <body>
     <div class="container">
      <form class="form-signin" method="post" action="/spring_security_01_war/login">
        <h2 class="form-signin-heading">Please sign in</h2>
        <p>
          <label for="username" class="sr-only">Username</label>
          <input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
        </p>
        <p>
          <label for="password" class="sr-only">Password</label>
          <input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
        </p>
<input name="_csrf" type="hidden" value="a81e8a84-36e7-4594-806b-bb2f43f5013b" />
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
      </form>
</div>
</body></html>

备注:

1)上边页面是spring-security内部内置拦截未认证时,跳转的认证页面。

2)其中配置信息对应的就是applicationContext-security.xml中配置的信息;

3)"_crsf"标签取决于配置“<security:csrf disabled="false"/>”;如果不想启用就配置为“<security:csrf disabled="true"/>”,此时页面中也不会输出该标签。

输入上边配置的内存账户、密码后,再次访问/admin就允许访问。

退出系统
访问’http://localhost:8080/spring_security_01_war/logout‘,之后会跳转到’http://localhost:8080/spring_security_01_war/index‘。

 

原文地址:https://www.cnblogs.com/yy3b2007com/p/12187868.html