Spring Security

Spring Security

一、简介

Spring Security从两个角度解决安全问题:

1)使用Servlet规范中的Filter保护Web请求并限制URL级别的访问。

2)使用Spring AOP保护方法调用-----借助于于对象代理和使用通知,

能够确保只有具备适当权限的用户才能访问安全保护的方法。

1.过滤Web请求

Spring Security借助一系列的Servlet Fliter来过滤web请求。我们只需要配置一个Filter。

DelegatingFilterProxy是一个特殊的Servlet Filter,它将工作委托给一个javax.servlet.Filter实现类,

这个实现类作为一个<bean>注册在Spring应用上下文中。

有一个名为springSecurityFilterChain的Filter bean, DelegatingFilterProxy会将过滤逻辑委托给它。

以Java方式配置DelegatingFilterProxy:

public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer{
}

配置好DelegatingFilterProxy后,它会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain bean。

2.编写简单的安全性配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
}

Spring Security必须配置在实现了WebSecurityConfigurger的bean中,或者

扩展WebSecurityConfigurerAdapter。

通过重载WebSecurityConfigurerAdapter中方法,可以指定web安全的细节。

二、用户存储

用户存储:也就是用户名、密码以及其他信息存储的地方,在进行决策的时候,会对其进行检索。

1.基于内存的用户存储

方式:重载WebSecurityConfigureAdapter中的configure方法。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        //启用内存用户存储
        //and链接多个用户配置
        auth.inMemoryAuthentication().withUser("user")
                .password("password").roles("USER").and()
                .withUser("tang").password("password").roles("USER", "ADMIN");
    }

3.基于数据库表进行认证

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    DataSource dataSource;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.jdbcAuthentication().dataSource(dataSource);
    }
}

重写默认的用户查询功能: 

Spring Security内部默认的查询sql:

配置自己的查询:

    @Autowired
    DataSource dataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username, password, flag "
                + "from user_info where username=?").authoritiesByUsernameQuery(
                        "select username, 'ROLE_USER' from user_info where username=?"
        );
    }

使用转码后的密码: 

上面的认证查询会预期密码存储在数据库中,如果数据库中的密码进行了转码的话,

那么认证就会失败,因为它与用户提交的明文密码并不匹配。为了解决这个问题,我们

需要借助passwordEncoder()方法指定一个密码转码器。

public interface PasswordEncoder {
    String encode(CharSequence var1);
    boolean matches(CharSequence var1, String var2);
    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username, password, flag "
                + "from user_info where username=?").authoritiesByUsernameQuery(
                        "select username, 'ROLE_USER' from user_info where username=?"
        ).passwordEncoder(new StandardPasswordEncoder("xsf"));
    }

 4.配置自定义的用户服务

假设需要认证的用户存储在非关系型数据库中,如Mongo或Neo4j,这种情况下

我们需要提供一个自定义的UserDetailService接口实现。

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

要做的就是实现loadUserByUsername()方法,根据给定的用户名查找用户。该接口不关心,也不知道底层所使用的用户存储。

为了使用UserDetailService来认证用户,我们可以通过AuthenticationManagerBuilder的userDetailsService()方法设置它。

三、拦截请求

方法:通过重载configure(HttpSecurity),为不同的URL路径有选择地应用安全性。

@Configuration
@EnableWebSecurity  //启用Spring Security的Web安全支持,并提供Spring MVC集成
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //定义了哪些URL路径应该被保护,哪些不应该
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }
}

authenticated()要求在执行该请求时,必须已经登陆了应用。如果用户没有认证的话,Spring Security的Filter将会捕获该请求,

并将用户重定向到应用的登陆页面。同时,permitAll()方法允许请求没有任何安全限制。

 1.使用Spring表达式进行安全保护

借助access()方法,可以将SpEL作为声明访问限制的一种方式。例如:

http.authorizeRequests()
                .antMatchers("/", "/home")
                .access("hasRole('ROLE_USER') and hasIpAddress(192.168.1.2)");

2.强制通道的安全性

传递到configure()方法中的HttpSecurity对象,有一个requiresChannel()方法,

借助这个方法能够为各种URL模式声明所要求的通道。

http.requiresChannel().antMatchers("/home").requiresSecure(); //需要https

四、认证用户

1.启用HTTP Basic认证

HTTP Basic认证会直接通过HTTP请求本身,对要访问的应用程序的用户进行认证。

本质上,它是一个HTTP401响应,表面必须要在请求中包含一个用户名和密码。

方法:在HttpSecurity对象上调用httpBasic()即可。

2.启用Remember-me功能

方法:在HttpSecurity对象上调用remeberMe()即可。

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login")
                .and()
                .rememberMe()
                .tokenValiditySeconds(2419200)
                .key("tangKey");

存储在cookie中的token包含用户名、密码、过期时间和一个私钥----在写入cookie前都

进行了MD5哈希。默认情况下,私钥名为SpringSecured,这里我们更改了它。

用户需要操作:

<input id="remeber_me" name="remeber_me" type="checkbox"/>

3.退出

退出功能是通过Servlet容器中的Filter实现的,这个Filter会拦截针对"/logout"的请求。添加退出功能:

<a th:href="@{/logout}">Logout</a>

如果希望用户被重定向到其他页面:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login")
                .and()
                .logout()
                .logoutSuccessUrl("/");     //退出成功后重定向到/
    }

五、保护视图

1.使用Spring Security的JSP标签

首先在JSP页面中声明该标签库:

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

Hello <security:authentication property="principal.username">

条件性的渲染内容: 

<sec:authorize access="hasRole('ROLE_TANG')"

因为已经在Spring Security中声明了安全性约束:

<security:authorize url="/admin">
    <spring:url value="/admin" var="admin_url" />
    <br/><a href="${admin_url}">Admin</a>
</security>
原文地址:https://www.cnblogs.com/Shadowplay/p/10572919.html