springboot学习15

Spring Security
一、配置Spring Security。
pom.xml 文件中引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

有几种配置 Spring Security 的方法,包括冗长的基于 xml 的配置。
基本的 Spring Security 配置类:

一个内存用户存储
基于 JDBC 的用户存储
由 LDAP 支持的用户存储
自定义用户详细信息服务

1、内存用户存储.

package com.example.web.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        auth
                .inMemoryAuthentication()
                .withUser("zhangsan")
                .password("{noop}pwd111")
                .authorities("ROLE_USER")
                .and()
                .withUser("wangwu")
                .password("{noop}pwd222")
                .authorities("ROLE_USER");
    }  
}

2、基于 JDBC 的用户存储
用户信息通常在关系数据库中维护,基于 JDBC 的用户存储似乎比较合适。
pom引入数据库相关依赖。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    

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

3、 LDAP 支持的用户存储

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
    auth
        .ldapAuthentication()
        	.userSearchFilter("(uid={0})")
        	.groupSearchFilter("member={0}");
}

4、自定义用户身份验证

二、web请求保护
1、保护请求
可以配置 HttpSecurity 的属性包括:

在允许服务请求之前,需要满足特定的安全条件
配置自定义登录页面
使用户能够退出应用程序
配置跨站请求伪造保护
package com.example.web.security;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.context.annotation.Configuration;
	@Configuration
	@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        // super.configure(http);
		// 确保 /design 和 /orders 的请求仅对经过身份验证的用户可用;
		// 1、对于 /design 和 /orders 的请求应该是授予 ROLE_USER 权限的用户的请求。
		// 2、所有的请求都应该被允许给所有的用户
		// 这些规则的顺序很重要。首先声明的安全规则优先于较低级别声明的安全规则。
		// 如果交换这两个安全规则的顺序,所有请求都将应用 permitAll(),那么关于 /design 和 /orders 请求的规则将不起作用。
        http
                .authorizeRequests()
                .antMatchers("/design", "/orders")
                .hasRole("ROLE_USER")
                .antMatchers("/", "/**").permitAll();
    }
}

所有可用方法:
大多数方法为请求处理提供了基本的安全规则,但是它们是自我限制的,只支持那些方法定义的安全规则。

方法
access(String) 如果 SpEL 表达式的值为 true,则允许访问
anonymous() 默认用户允许访问
authenticated() 认证用户允许访问
denyAll() 无条件拒绝所有访问
fullyAuthenticated() 如果用户是完全授权的(不是记住用户),则允许访问
hasAnyAuthority(String…) 如果用户有任意给定的权限,则允许访问
hasAnyRole(String…) 如果用户有任意给定的角色,则允许访问
hasAuthority(String) 如果用户有给定的权限,则允许访问
hasIpAddress(String) 来自给定 IP 地址的请求允许访问
hasRole(String) 如果用户有给定的角色,则允许访问
not() 拒绝任何其他访问方法
permitAll() 无条件允许访问
rememberMe() 允许认证了的同时标记了记住我的用户访问

可以使用 access() 方法提供 SpEL 表达式来声明更丰富的安全规则。
Spring Security 对 SpEL 的扩展:

Security 表达式
authentication 用户认证对象
denyAll 通常值为 false
hasAnyRole(list of roles) 如果用户有任何给定的角色,则为 true
hasRole(role) 如果用户有给定的角色,则为 true
hasIpAddress(IP Address) 如果请求来自给定 IP 地址,则为 true
isAnonymous() 如果用户是默认用户,则为 true
isAuthenticated() 如果用户是认证了的,则为 true
isFullyAuthenticated() 如果用户被完全认证了的(不是使用记住我进行认证),则为 true
isRememberMe() 如果用户被标记为记住我后认证了,则为 true
permitAll() 通常值为 true
principal 用户 pricipal 对象

如使用access() 方法以及 hasRole() 和 permitAll 表达式:

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    
    http
        .authorizeRequests()
        .antMatchers("/design", "/orders")
            .access("hasRole('ROLE_USER')")
        .antMatchers(/, "/**").access("permitAll");
}

又如,只想允许具有 ROLE_USER 权限的用户在周二创建新的 Taco;:

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    
    http
        .authorizeRequests()
            .antMatchers("/design", "/orders")
                .access("hasRole('ROLE_USER') && " +
                        "T(java.util.Calendar).getInstance().get("+
                        "T(java.util.Calendar).DAY_OF_WEEK) == " +
                        "T(java.util.Calendar).TUESDAY")
            .antMatchers(/, "/**").access("permitAll");
}

2、创建用户登录页面

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    
    http
        .authorizeRequests()
            .antMatchers("/design", "/orders")
                .access("hasRole('ROLE_USER')")
            .antMatchers(/, "/**").access("permitAll")
        
        .and() // and() 方法表示已经完成了授权配置,开始新的配置部分时,将多次使用 and()。
            .formLogin()
            .loginPage("/login")
            .loginProcessingUrl("/authenticate") // 指定 Spring Security 应该监听请求 /authenticate 请求以处理登录提交
	        .usernameParameter("user") // 用户名和密码字段现在应该命名为 user 和 pwd
    	    .passwordParameter("pwd")
    	    .defaultSuccessUrl("/design"); // 成功的登录将直接将到指定的一个默认的页面,如design。
			//.defaultSuccessUrl("/design", true); // 可以强制用户在登录后进入设计页面,即使他们在登录之前已经在其他地方导航,方法是将 true 作为第二个参数传递给 defaultSuccessUrl
}

3、登出

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    
http
    .authorizeRequests()
	    .antMatchers("/design", "/orders")
	          .access("hasRole('ROLE_USER')")
	    .antMatchers(/, "/**").access("permitAll")        
	.and()
	    .logout()
	        .logoutSuccessUrl("/");
}

当用户单击登出按钮时,他们的 session 将被清除,他们将退出应用程序。默认情况下,它们将被重定向到登录页面。如果希望它们被发送到另一个页面,可以调用 logoutSuccessUrl() 来指定一个不同的登出后的登录页面。

4、阻止CSRF请求伪造

为了防止此类攻击,应用程序可以在显示表单时生成 CSRF token,将该 token 放在隐藏字段中,
然后将其存储在服务器上供以后使用。
提交表单时,token 将与其他表单数据一起发送回服务器。
然后服务器拦截请求,并与最初生成的 token 进行比较。如果 token 匹配,则允许继续执行请求。
否则,表单一定是由一个不知道服务器生成的 token的恶意网站呈现的

Spring Security 有内置的 CSRF 保护。默认启用

在 Thymeleaf 模板的一个隐藏字段中呈现 CSRF token:

<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
如果使用 Spring MVC 的 JSP 标签库或带有 Spring 安全方言的 Thymeleaf,
那么甚至不需要显式地包含一个隐藏字段,隐藏字段将自动呈现。
在 Thymeleaf 中,只需确保 <form> 元素的一个属性被前缀为 Thymeleaf 属性。

如:Thymeleaf 渲染隐藏字段所需要的仅仅是 th:action 属性。

<form method="POST" th:action="@{/login}" id="loginForm">

禁用csrf:

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    
	http
	  .authorizeRequests()
	    .antMatchers("/design", "/orders")
	          .access("hasRole('ROLE_USER')")
	    .antMatchers(/, "/**").access("permitAll")        
	 .and()
	     .csrf()
	         .disable();
}

5、获得认证用户的信息

通过 SecurityContext 对象(从 SecurityContextHolder. getcontext() 中返回)或
使用 @Authentication Principal 注入控制器中,可以获得认证用户的信息。

会将与安全性无关的代码与安全代码一起丢弃:

@PostMapping
public String processOrder(@Valid Order order, Errors errors,
                           SessionStatus sessionStatus,
                           Principal principal) {
    
    
    ...
    
    User user = userRepository.findByUsername(principal.getName());
    order.setUser(user);
    
    ...
}

可以接收Authentication 对象,调用 getPrincipal() 来获取主体对象:

@PostMapping
public String processOrder(@Valid Order order, Errors errors,
                           SessionStatus sessionStatus,
                           Authentication authentication) {
    
    
    ...
        
    User user = (User) authentication.getPrincipal();
    order.setUser(user);
    
    ...
}

@AuthenticationPrincipal 的优点在于它不需要强制转换:

@PostMapping
public String processOrder(@Valid Order order, Errors errors,
                           SessionStatus sessionStatus,
                           @AuthenticationPrincipal User user) {
    
    
    if (errors.hasErrors()) {
    
    
        return "orderForm";
    }
    
    order.setUser(user);
    
    orderRepo.save(order);
    sessionStatus.setComplete();
    
    return "redirect:/";
}

从安全上下文获取一个认证对象:

Authentication authentication =
    SecurityContextHolder.getContext().getAuthentication();
User user = (User) authentication.getPrincipal();

猜你喜欢

转载自blog.csdn.net/tongwudi5093/article/details/113790303