[SpringSecurity] 入门简介

前言

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

接下来我们先来个入门案例,先学会简单实用 SpringSecurity

入门案例

在 pom.xml 中引入 Spring 以及 SpringSecurity

        
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-webartifactId>
            <version>4.1.0.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-configartifactId>
            <version>4.1.0.RELEASEversion>
        dependency>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <version>${spring.version}version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webartifactId>
            <version>${spring.version}version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>${spring.version}version>
        dependency>

配置web.xml,引入SpringSecurity的Filter


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <context-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>classpath:spring-security.xmlparam-value>
    context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        listener-class>
    listener>
    
    <filter>
        
        <filter-name>springSecurityFilterChainfilter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
    filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChainfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
web-app>

配置一个最简单的 spring-security.xml


<beans:beans xmlns="http://www.springframework.org/schema/security"
       xmlns:beans="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    
    <http use-expressions="true">
        
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        
        <form-login/>
    http>

    
    <authentication-manager>
        <authentication-provider>
            <user-service>
                
                <user name="admin" password="123456" authorities="ROLE_USER" />
            user-service>
        authentication-provider>
    authentication-manager>

beans:beans>

现在我们启动Maven工程,访问 http://localhost:8888/security2/login
[SpringSecurity] 入门简介_第1张图片

配置 spring-security.xml

1> 修改默认登陆页

新增一个登录页 login.html

注:必须用POST提交到login


<form action='login' method='POST'>
    <table>
        <tr>
            <td>用户名:td>
            <td><input type='text' name='user' value=''>td>
        tr>
        <tr>
            <td>密码:td>
            <td><input type='password' name='pass' />td>
        tr>
        <tr>
            <td colspan='2'><input name="submit" type="submit"
                                   value="登录" />td>
        tr>
    table>
form>

删除之前的

<http use-expressions="true">
    
    
http>

修改为

<http use-expressions="true">
    
        
        <form-login login-page="/login.html" default-target-url="/index.html"
                    always-use-default-target="true"
                    authentication-failure-url="/index.html"
                    username-parameter="user" password-parameter="pass"/>
http>

允许/login.html匿名访问



<http pattern="/login.html" security="none">http>

2> CSRF

此时就可以继续启动Maven工程访问了,但是这时会给我们报一个403 CSRF 错误

HTTP Status 403 - Could not verify the provided CSRF 
token because your session was not found.

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。

注:CSRF一般只拦截POST请求,因为POST请求会改数据,GET最多是获取数据

解决方案

修改 spring-security.xml

<http use-expressions="true">
    
    <csrf disabled="true"/>
http>

此时就可以继续登录了
`

3> 登出

修改 spring-security.xml

<http use-expressions="true">
    
    <logout logout-url="/logout" logout-success-url="/login.html"/>
http>

4> iframe

SpringSecurity默认是会拦截 iframe 的引入,如果页面中引入了 ifame,应开启该选项

<http use-expressions="true">
        <headers>
            
            <frame-options policy="SAMEORIGIN">frame-options>
        headers>
http>

自定义认证类

通过以上配置我们发现登录时的账号、密码、权限都是写死在 spring-security.xml 里的

现在我们配置一个自定义认证类,来实现动态查询数据库登录

创建类UserDetailsServiceImpl.java 实现UserDetailsService接口

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    //@Reference
    //private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        // username -> 用户页面提交的账号
        // select db ...
        // 相关权限
        List lists = new ArrayList<>();
        lists.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(username,"123456",lists);
    }
}

修改 spring-security.xml 的认证配置


<context:component-scan base-package="com.sino"/>
<authentication-manager>
        <authentication-provider user-service-ref="userDetailsServiceImpl">
        authentication-provider>
authentication-manager>

这样我们就可以实现自定义认证了

SpringSecurity权限注解

SpringSecutity 在权限控制中有多个注解

@PreAuthorize,@PostAuthorize,@Secured,@RolesAllowed

@PreAuthorize,@PostAuthorize 是方法级注解,分别在方法调用前后执行权限检查

部分注解 @PreAuthorize,@PostAuthorize 支持 Spring EL 表达式

例如: hasRole, hasAnyRole, hasPermission

修改 spring-security.xml 开启注解

<global-method-security
        jsr250-annotations="enabled"
        pre-post-annotations="enabled"
        secured-annotations="enabled">
</global-method-security>
  • prePostEnabled :决定Spring Security的前注解是否可用 [@PreAuthorize,@PostAuthorize,..]
  • secureEnabled : 决定是否Spring Security的保障注解 [@Secured] 是否可用
  • jsr250Enabled :决定 JSR-250 annotations 注解[@RolesAllowed..] 是否可用.

接下来为了便于测试,我们先把SpringMVC配置一下,修改web.xml,增加DispatcherServlet

    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:spring-security.xmlparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>

    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

修改 spring-security.xml 开启MVC

    <context:component-scan base-package="com.sino"/>
    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>

测试Controller

@RestController
public class TestController {
    @RequestMapping("/test")
    @PreAuthorize("hasRole('ROLE_USER1')")
    public String test(){
        return "test";
    }
}

Permission

SpringSecurity 的 permission 默认是返回 false,如果要使用 hasPermission 进行权限检查,

我们要写一个自定义类实现 PermissionEvaluator 放入 DefaultMethodSecurityExpressionHandler

public class MyPermissionEvaluator implements PermissionEvaluator {

    @Override
    public boolean hasPermission(Authentication authentication,
                                 Object targetDomainObject, Object permission) {
        if (targetDomainObject.equals("user")) {
            return this.hasPermission(authentication, permission);
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication,
                                 Serializable serializable, String s, Object o) {
        return false;
    }

    /**
     * 匹配权限
     */
    private boolean hasPermission(Authentication authentication, Object permission) {
        Collection authorities
                = authentication.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            if (authority.getAuthority().equals(permission)) {
                return true;
            }
        }
        return false;
    }
}

修改 spring-security.xml

    
    <beans:bean id="expressionHandler"
                class="org.springframework.security.access.
                expression.method.DefaultMethodSecurityExpressionHandler">
        <beans:property name="permissionEvaluator" ref="myPermissionEvaluator" />
    beans:bean>
    
    <beans:bean id="myPermissionEvaluator"
                class="com.sino.service.MyPermissionEvaluator"/>

    <global-method-security
            jsr250-annotations="enabled"
            pre-post-annotations="enabled"
            secured-annotations="enabled">
        
        <expression-handler ref="expressionHandler" />
    global-method-security>

测试Controller

@RestController
public class TestController {
    @RequestMapping("/test")
    @PreAuthorize("hasPermission('user','ROLE_USER')")
    public String test(){
        return "test";
    }
}

完整代码

通过以上介绍,SpringSecurity 的入门简介就说完了,下面是所有代码

pom.xml

    <properties>
        <spring.version>4.2.4.RELEASEspring.version>
    properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-webartifactId>
            <version>4.1.0.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-configartifactId>
            <version>4.1.0.RELEASEversion>
        dependency>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <version>${spring.version}version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webartifactId>
            <version>${spring.version}version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>${spring.version}version>
        dependency>
    dependencies>

web.xml


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <context-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>classpath:spring-security.xmlparam-value>
    context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
    listener>
    
    <filter>
        
        <filter-name>springSecurityFilterChainfilter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
    filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChainfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>

    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:spring-security.xmlparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>

    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

web-app>

spring-security.xml


<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="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"
             xsi:schemaLocation="http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.sino"/>
    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>

    
    <http pattern="/login.html" security="none">http>

    
    
    <http use-expressions="true">
        
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />

        
        
        <form-login login-page="/login.html" default-target-url="/index.html"
                    always-use-default-target="true"
                    authentication-failure-url="/index.html"
                    username-parameter="user" password-parameter="pass"/>
        
        <csrf disabled="true"/>
        
        <headers>
            
            <frame-options policy="SAMEORIGIN">frame-options>
        headers>
        
        <logout logout-url="/logout" logout-success-url="/login.html"/>
    http>

    
    <authentication-manager>
        <authentication-provider user-service-ref="userDetailsServiceImpl">
        authentication-provider>
    authentication-manager>

    
    <beans:bean id="expressionHandler"
                class="org.springframework.security.access.
                expression.method.DefaultMethodSecurityExpressionHandler">
        <beans:property name="permissionEvaluator" ref="myPermissionEvaluator" />
    beans:bean>
    
    <beans:bean id="myPermissionEvaluator"
                class="com.sino.service.MyPermissionEvaluator"/>
    
    <global-method-security
            jsr250-annotations="enabled"
            pre-post-annotations="enabled"
            secured-annotations="enabled">
        <expression-handler ref="expressionHandler" />
    global-method-security>

beans:beans>

MyPermissionEvaluator.java

public class MyPermissionEvaluator implements PermissionEvaluator {

    @Override
    public boolean hasPermission(Authentication authentication,
                                 Object targetDomainObject, Object permission) {
        if (targetDomainObject.equals("user")) {
            return this.hasPermission(authentication, permission);
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication,
                                 Serializable serializable, String s, Object o) {
        return false;
    }

    /**
     * 匹配权限
     */
    private boolean hasPermission(Authentication authentication, Object permission) {
        Collection authorities
                = authentication.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            if (authority.getAuthority().equals(permission)) {
                return true;
            }
        }
        return false;
    }
}

UserDetailsServiceImpl.java

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    //@Reference
    //private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        // username -> 用户页面提交的账号
        // select db ...
        // 相关权限
        List lists = new ArrayList<>();
        lists.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(username,"111111",lists);
    }
}

TestController.java

@RestController
public class TestController {
    @RequestMapping("/test")
    //@PreAuthorize("hasRole('ROLE_USER')")
    @PreAuthorize("hasPermission('user','ROLE_USER')")
    public String test(){
        return "test";
    }
}

至此,SpringSecurity 的入门简介就说完了~

你可能感兴趣的:(安全)