Spring security 简单使用笔记

Spring security 是一个能高度定制的身份验证和访问控制框架, 是基于 Spring 应用程序标准.
相似的的框架有 Shrio, 相比于 Spring security, Shrio 更轻一些. Spring secuity 对于 Spring 框架的兼容性更好, 但同时也更依赖于 Spring 框架.

关键过滤器

FilterSecurityInterceptor

org.springframework.security.web.access.intercept.FilterSecurityInterceptor

方法级权限过滤器,位于过滤链最底部

ExceptionTranslationFilter

org.springframework.security.web.access.ExceptionTranslationFilter

异常过滤器,处理认证授权过程中抛出的异常

UsernamePasswordAuthenticationFilter

org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

校验/login(POST)请求提交的用户名和密码

过滤器加载流程

使用Spring Security配置过滤器(懒加载)

  1. org.springframework.web.filter.DelegatingFilterProxy 类的 doFilter() 方法中执行Filter initDelegate(WebApplicationContext wac) 方法来获取org.springframework.security.web.FilterChainProxy这个过滤器链
  2. org.springframework.security.web.FilterChainProxy 类的 doFilter() 方法中 执行 void doFilterInternal(ServletRequest request‚ ServletResponse response‚ FilterChain chain) 方法来注册Spring Security的过滤器

涉及到的过滤器

DisableEncodeUrlFilter

org.springframework.security.web.session.DisableEncodeUrlFilter

Disables encoding URLs using the HttpServletResponse to prevent including the session id in URLs which is not considered URL because the session id can be leaked in things like HTTP access logs.

翻译

禁用使用HttpServletResponse对URL进行编码,以防止在URL中包含会话id,因为会话id可能会在HTTP访问日志等内容中泄漏,因此不被视为URL。

WebAsyncManagerIntegrationFilter

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter

Provides integration between the SecurityContext and Spring Web’s WebAsyncManager by using the SecurityContextCallableProcessingInterceptor.beforeConcurrentHandling(org.springframework.web.context.request.NativeWebRequest, Callable) to populate the SecurityContext on the Callable.

翻译

通过使用SecurityContextCallableProcessingInterceptor.beforencurrentHandling(org.springframework.Web.context.request.NativeWebRequest,Callable)在可调用对象上填充SecurityContext,提供SecurityContext和Spring Web的WebAsyncManager之间的集成。

SecurityContextPersistenceFilter

org.springframework.security.web.context.SecurityContextPersistenceFilter

Populates the SecurityContextHolder with information obtained from the configured SecurityContextRepository prior to the request and stores it back in the repository once the request has completed and clearing the context holder. By default it uses an HttpSessionSecurityContextRepository. See this class for information HttpSession related configuration options.
This filter will only execute once per request, to resolve servlet container (specifically Weblogic) incompatibilities.
This filter MUST be executed BEFORE any authentication processing mechanisms. Authentication processing mechanisms (e.g. BASIC, CAS processing filters etc) expect the SecurityContextHolder to contain a valid SecurityContext by the time they execute.
This is essentially a refactoring of the old HttpSessionContextIntegrationFilter to delegate the storage issues to a separate strategy, allowing for more customization in the way the security context is maintained between requests.
The forceEagerSessionCreation property can be used to ensure that a session is always available before the filter chain executes (the default is false, as this is resource intensive and not recommended).

翻译

使用请求之前从配置的SecurityContextRepository获得的信息填充SecurityContextHolder,并在请求完成并清除上下文持有者后将其存储回存储库中。默认情况下,它使用HttpSessionSecurityContextRepository。有关HttpSession相关配置选项的信息,请参阅此类。
每个请求只执行一次该过滤器,以解决servlet容器(特别是Weblogic)不兼容问题。
必须在任何身份验证处理机制之前执行此筛选器。身份验证处理机制(例如BASIC、CAS处理过滤器等)期望SecurityContextHolder在执行时包含有效的SecurityContext。
这本质上是对旧HttpSessionContextIntegrationFilter的重构,以将存储问题委托给一个单独的策略,从而允许在请求之间维护安全上下文的方式进行更多的定制。
forceEagleSessionCreation属性可用于确保在筛选器链执行之前会话始终可用(默认值为false,因为这是资源密集型的,不建议使用)。

已弃用,替代者: SecurityContextHolderFilter:

org.springframework.security.web.context.SecurityContextHolderFilter

A javax.servlet.Filter that uses the SecurityContextRepository to obtain the SecurityContext and set it on the SecurityContextHolder. This is similar to SecurityContextPersistenceFilter except that the SecurityContextRepository.saveContext(SecurityContext, HttpServletRequest, HttpServletResponse) must be explicitly invoked to save the SecurityContext. This improves the efficiency and provides better flexibility by allowing different authentication mechanisms to choose individually if authentication should be persisted.

翻译

javax.servlet。使用SecurityContextRepository获取SecurityContext并在SecurityContextHolder上设置它的筛选器。除了SecurityContextRepository之外,这与SecurityContextPersistenceFilter类似。必须显式调用saveContext(SecurityContext、HttpServletRequest、HttpServletsResponse)来保存SecurityContext。这通过允许不同的身份验证机制单独选择是否应持久化身份验证,提高了效率并提供了更好的灵活性。

HeaderWriterFilter

org.springframework.security.web.header.HeaderWriterFilter

Filter implementation to add headers to the current response. Can be useful to add certain headers which enable browser protection. Like X-Frame-Options, X-XSS-Protection and X-Content-Type-Options.

翻译

筛选器实现以向当前响应添加标头。添加启用浏览器保护的某些标头可能很有用。与X-Frame-Options、X-XSS-Protect和X-Content-Type-Options类似。

CsrfFilter

org.springframework.security.web.csrf.CsrfFilter

Applies CSRF protection using a synchronizer token pattern. Developers are required to ensure that CsrfFilter is invoked for any request that allows state to change. Typically this just means that they should ensure their web application follows proper REST semantics (i.e. do not change state with the HTTP methods GET, HEAD, TRACE, OPTIONS).
Typically the CsrfTokenRepository implementation chooses to store the CsrfToken in HttpSession with HttpSessionCsrfTokenRepository wrapped by a LazyCsrfTokenRepository. This is preferred to storing the token in a cookie which can be modified by a client application.

翻译

使用同步器令牌模式应用CSRF保护。开发人员必须确保对任何允许状态更改的请求调用CsrfFilter。通常,这意味着他们应该确保他们的web应用程序遵循正确的REST语义(即,不要使用HTTP方法GET、HEAD、TRACE和OPTIONS更改状态)。
通常,CsrfTokenRepository实现选择将CsrfToken存储在HttpSession中,HttpSessionCsrfTokenRepository由LazySrfTokenStore封装。这比将令牌存储在可由客户端应用程序修改的cookie中更可取。

LogoutFilter

org.springframework.security.web.authentication.logout.LogoutFilter

Logs a principal out.
Polls a series of LogoutHandlers. The handlers should be specified in the order they are required. Generally you will want to call logout handlers TokenBasedRememberMeServices and SecurityContextLogoutHandler (in that order).
After logout, a redirect will be performed to the URL determined by either the configured LogoutSuccessHandler or the logoutSuccessUrl, depending on which constructor was used.

翻译

注销委托人。
轮询一系列LogoutHandler。应按照所需的顺序指定处理程序。通常,您需要调用注销处理程序TokenBasedRememberMeServices和SecurityContextLogoutHandler(按顺序)。
注销后,将根据使用的构造函数,重定向到由配置的LogoutSuccessHandler或LogoutSucceUrl确定的URL。

UsernamePasswordAuthenticationFilter

org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

Processes an authentication form submission. Called AuthenticationProcessingFilter prior to Spring Security 3.0.
Login forms must present two parameters to this filter: a username and password. The default parameter names to use are contained in the static fields SPRING_SECURITY_FORM_USERNAME_KEY and SPRING_SECURITY_FORM_PASSWORD_KEY. The parameter names can also be changed by setting the usernameParameter and passwordParameter properties.
This filter by default responds to the URL /login.

翻译

处理身份验证表单提交。在Spring Security 3.0之前调用AuthenticationProcessingFilter。
登录表单必须为此筛选器提供两个参数:用户名和密码。要使用的默认参数名包含在静态字段SPRING_SECURITY_FORM_USERNAME_KEY和SPRING_SETURITY_FFORM_PASSWORD_KEY中。还可以通过设置usernameParameter和passwordParameter属性来更改参数名称。
默认情况下,此筛选器响应URL/登录。

DefaultLoginPageGeneratingFilter

org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter

For internal use with namespace configuration in the case where a user doesn’t configure a login page. The configuration code will insert this filter in the chain instead. Will only work if a redirect is used to the login page.

翻译

在用户未配置登录页面的情况下,用于内部命名空间配置。配置代码将在链中插入此筛选器。仅当重定向到登录页面时才有效。

DefaultLogoutPageGeneratingFilter

org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter

Generates a default log out page.

翻译

生成默认注销页面。

BasicAuthenticationFilter

org.springframework.security.web.authentication.www.BasicAuthenticationFilter

Processes a HTTP request’s BASIC authorization headers, putting the result into the SecurityContextHolder.
For a detailed background on what this filter is designed to process, refer to RFC 1945, Section 11.1 . Any realm name presented in the HTTP request is ignored.
In summary, this filter is responsible for processing any request that has a HTTP request header of Authorization with an authentication scheme of Basic and a Base64-encoded username:password token. For example, to authenticate user “Aladdin” with password “open sesame” the following header would be presented:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
This filter can be used to provide BASIC authentication services to both remoting protocol clients (such as Hessian and SOAP) as well as standard user agents (such as Internet Explorer and Netscape).
If authentication is successful, the resulting Authentication object will be placed into the SecurityContextHolder.
If authentication fails and ignoreFailure is false (the default), an AuthenticationEntryPoint implementation is called (unless the ignoreFailure property is set to true). Usually this should be BasicAuthenticationEntryPoint, which will prompt the user to authenticate again via BASIC authentication.
Basic authentication is an attractive protocol because it is simple and widely deployed. However, it still transmits a password in clear text and as such is undesirable in many situations. Digest authentication is also provided by Spring Security and should be used instead of Basic authentication wherever possible. See DigestAuthenticationFilter.
Note that if a RememberMeServices is set, this filter will automatically send back remember-me details to the client. Therefore, subsequent requests will not need to present a BASIC authentication header as they will be authenticated using the remember-me mechanism.

翻译

处理HTTP请求的BASIC授权头,将结果放入SecurityContextHolder。
有关此筛选器设计处理的详细背景信息,请参阅RFC 1945第11.1节。HTTP请求中出现的任何领域名称都将被忽略。
总之,此筛选器负责处理具有HTTP请求头Authorization、身份验证方案为Basic和Base64编码的用户名:密码令牌的任何请求。例如,要使用密码“open芝麻”验证用户“Aladdin”,将显示以下标头:
授权:基本QWxhZGRpbjpvcGVuIHNlc2FtZQ==
此筛选器可用于向远程协议客户端(如Hessian和SOAP)以及标准用户代理(如Internet Explorer和Netscape)提供BASIC身份验证服务。
如果身份验证成功,则生成的身份验证对象将被放置到SecurityContextHolder中。
如果身份验证失败且ignoreFailure为false(默认值),则调用AuthenticationEntryPoint实现(除非ignoreFailion属性设置为true)。通常这应该是BasicAuthenticationEntryPoint,它将提示用户通过BASIC身份验证再次进行身份验证。
基本身份验证是一个有吸引力的协议,因为它简单且部署广泛。然而,它仍然以明文形式传输密码,因此在许多情况下是不可取的。摘要式身份验证也由SpringSecurity提供,应尽可能使用它来代替基本身份验证。请参阅DigestAuthenticationFilter。
请注意,如果设置了RememberMeServices,则此过滤器将自动向客户端发回记住我的详细信息。因此,后续请求不需要呈现BASIC认证头,因为它们将使用记住我的机制进行认证。

RequestCacheAwareFilter

org.springframework.security.web.savedrequest.RequestCacheAwareFilter

Responsible for reconstituting the saved request if one is cached and it matches the current request.
It will call getMatchingRequest on the configured RequestCache. If the method returns a value (a wrapper of the saved request), it will pass this to the filter chain’s doFilter method. If null is returned by the cache, the original request is used and the filter has no effect.

翻译

负责重新构建保存的请求,如果其中一个请求被缓存并且与当前请求匹配。
它将在配置的RequestCache上调用getMatchingRequest。如果该方法返回一个值(保存的请求的包装器),它会将其传递给过滤器链的doFilter方法。如果缓存返回null,则使用原始请求,过滤器无效。

SecurityContextHolderAwareRequestFilter

org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter

A Filter which populates the ServletRequest with a request wrapper which implements the servlet API security methods.
SecurityContextHolderAwareRequestWrapper is extended to provide the following additional methods:

  • HttpServletRequest.authenticate(HttpServletResponse) - Allows the user to determine if they are authenticated and if not send the user to the login page. See setAuthenticationEntryPoint(AuthenticationEntryPoint).
  • HttpServletRequest.login(String, String) - Allows the user to authenticate using the AuthenticationManager. See setAuthenticationManager(AuthenticationManager).
  • HttpServletRequest.logout() - Allows the user to logout using the LogoutHandlers configured in Spring Security. See setLogoutHandlers(List).
  • AsyncContext.start(Runnable) - Automatically copy the SecurityContext from the SecurityContextHolder found on the Thread that invoked AsyncContext.start(Runnable) to the Thread that processes the Runnable.
翻译

一个过滤器,用实现servlet API安全方法的请求包装器填充ServletRequest。
SecurityContextHolderAwareRequestWrapper被扩展以提供以下附加方法:

  • HttpServletRequest.authenticate(HttpServletResponse)-允许用户确定他们是否经过身份验证,如果没有,则将用户发送到登录页面。请参见setAuthenticationEntryPoint(AuthenticationEntrePoint)。
  • HttpServletRequest.login(String,String)-允许用户使用AuthenticationManager进行身份验证。请参阅setAuthenticationManager(AuthenticationManager)。
  • HttpServletRequest.logout()-允许用户使用Spring Security中配置的LogoutHandlers注销。请参见setLogoutHandlers(列表)。
  • AsyncContext.start(Runnable)-自动从调用AsyncContext的线程上找到的SecurityContextHolder复制SecurityContext。启动(Runnable)到处理Runnable的线程。

AnonymousAuthenticationFilter

rg.springframework.security.web.authentication.AnonymousAuthenticationFilter

Detects if there is no Authentication object in the SecurityContextHolder, and populates it with one if needed.

翻译

检测SecurityContextHolder中是否没有身份验证对象,并在需要时用一个身份验证对象填充。

SessionManagementFilter

org.springframework.security.web.session.SessionManagementFilter

Detects that a user has been authenticated since the start of the request and, if they have, calls the configured SessionAuthenticationStrategy to perform any session-related activity such as activating session-fixation protection mechanisms or checking for multiple concurrent logins.

翻译

检测用户自请求开始以来是否已通过身份验证,如果已通过,则调用配置的SessionAuthenticationStrategy以执行任何与会话相关的活动,例如激活会话固定保护机制或检查多个并发登录。

ExceptionTranslationFilter

org.springframework.security.web.access.ExceptionTranslationFilter

Handles any AccessDeniedException and AuthenticationException thrown within the filter chain.
This filter is necessary because it provides the bridge between Java exceptions and HTTP responses. It is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.
If an AuthenticationException is detected, the filter will launch the authenticationEntryPoint. This allows common handling of authentication failures originating from any subclass of org.springframework.security.access.intercept.AbstractSecurityInterceptor.
If an AccessDeniedException is detected, the filter will determine whether or not the user is an anonymous user. If they are an anonymous user, the authenticationEntryPoint will be launched. If they are not an anonymous user, the filter will delegate to the AccessDeniedHandler. By default the filter will use AccessDeniedHandlerImpl.
To use this filter, it is necessary to specify the following properties:

  • authenticationEntryPoint indicates the handler that should commence the authentication process if an AuthenticationException is detected. Note that this may also switch the current protocol from http to https for an SSL login.
  • requestCache determines the strategy used to save a request during the authentication process in order that it may be retrieved and reused once the user has authenticated. The default implementation is HttpSessionRequestCache.
翻译

处理筛选器链中引发的任何AccessDeniedException和AuthenticationException。
这个过滤器是必要的,因为它提供了Java异常和HTTP响应之间的桥梁。它只关心维护用户界面。此筛选器不执行任何实际的安全强制。
如果检测到AuthenticationException,则过滤器将启动authenticationEntryPoint。这允许对源自org.springframework.security.access.intercept.AbstractSecurityInterceptor的任何子类的身份验证失败进行常见处理。
如果检测到AccessDeniedException,则筛选器将确定用户是否为匿名用户。如果他们是匿名用户,将启动authenticationEntryPoint。如果他们不是匿名用户,则筛选器将委托给AccessDeniedHandler。默认情况下,过滤器将使用AccessDeniedHandlerImpl。
要使用此筛选器,必须指定以下属性:

  • authenticationEntryPoint指示在检测到AuthenticationException时应开始身份验证过程的处理程序。注意,对于SSL登录,这也可能会将当前协议从http切换为https。
  • requestCache确定用于在身份验证过程中保存请求的策略,以便在用户进行身份验证后可以检索和重用该请求。默认实现是HttpSessionRequestCache。

FilterSecurityInterceptor

org.springframework.security.web.access.intercept.FilterSecurityInterceptor

Performs security handling of HTTP resources via a filter implementation.
The SecurityMetadataSource required by this security interceptor is of type FilterInvocationSecurityMetadataSource.
Refer to AbstractSecurityInterceptor for details on the workflow.

翻译

通过过滤器实现执行HTTP资源的安全处理。
此安全拦截器所需的SecurityMetadataSource的类型为FilterInvocationSecurityMetadataSource。
有关工作流的详细信息,请参阅AbstractSecurityInterceptor。

实现自定义的账号密码验证逻辑

创建UsernamePasswordAuthenticationFilter子类

重写下列方法

  • Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  • void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
  • void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)

attemptAuthentication方法

Performs actual authentication.
The implementation should do one of the following:
-Return a populated authentication token for the authenticated user, indicating successful authentication
-Return null, indicating that the authentication process is still in progress. Before returning, the implementation should perform any additional work required to complete the process.
-Throw an AuthenticationException if the authentication process fails

翻译

执行实际身份验证。
实施应执行以下操作之一:

  • 为经过身份验证的用户返回填充的身份验证令牌,表示身份验证成功
  • 返回null,表示身份验证过程仍在进行中。在返回之前,实施应执行完成流程所需的任何额外工作。
  • 如果身份验证过程失败,则引发AuthenticationException

successfulAuthentication方法

Default behaviour for successful authentication:

  • Sets the successful Authentication object on the SecurityContextHolder
  • Informs the configured RememberMeServices of the successful login
  • Fires an InteractiveAuthenticationSuccessEvent via the configured ApplicationEventPublisher
  • Delegates additional behaviour to the AuthenticationSuccessHandler.
    Subclasses can override this method to continue the FilterChain after successful authentication.
翻译

成功身份验证的默认行为:

  • 在SecurityContextHolder上设置成功的身份验证对象
  • 通知已配置的RememberMeServices成功登录
  • 通过配置的ApplicationEventPublisher激发InteractiveAuthenticationSuccessEvent
  • 将其他行为委托给AuthenticationSuccessHandler。
    子类可以重写此方法以在成功身份验证后继续FilterChain。

unsuccessfulAuthentication方法

Default behaviour for unsuccessful authentication:
Clears the SecurityContextHolder
Stores the exception in the session (if it exists or allowSesssionCreation is set to true)
Informs the configured RememberMeServices of the failed login
Delegates additional behaviour to the AuthenticationFailureHandler.

翻译

身份验证失败的默认行为:

  • 清除SecurityContextHolder
  • 将异常存储在会话中(如果它存在或allowSesssionCreation设置为true)
  • 通知配置的RememberMeServices登录失败
  • 将其他行为委托给AuthenticationFailureHandler。

实现 UserDetailsService 接口

org.springframework.security.core.userdetails.UserDetailsService

oadUserByUsername方法

UserDetails loadUserByUsername(String username)

Locates the user based on the username. In the actual implementation, the search may possibly be case sensitive, or case insensitive depending on how the implementation instance is configured. In this case, the UserDetails object that comes back may have a username that is of a different case than what was actually requested..

翻译

根据用户名查找用户。在实际实现中,搜索可能区分大小写,或者不区分大小写(取决于实现实例的配置方式)。在这种情况下,返回的UserDetails对象的用户名可能与实际请求的用户名不同。

实现 PasswordEncoder 接口

org.springframework.security.crypto.password.PasswordEncoder

encode方法

String encode(CharSequence rawPassword)

Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or greater hash combined with an 8-byte or greater randomly generated salt.

翻译

对原始密码进行编码。通常,一个好的编码算法应用SHA-1或更大的散列,并结合8字节或更大随机生成的盐。

matches方法

boolean matches(CharSequence rawPassword‚ String encodedPassword)

Verify the encoded password obtained from storage matches the submitted raw password after it too is encoded. Returns true if the passwords match, false if they do not. The stored password itself is never decoded.

翻译

验证从存储中获得的编码密码与提交的原始密码是否匹配。如果密码匹配,则返回true,否则返回false。存储的密码本身永远不会被解码。

upgradeEncoding方法

boolean upgradeEncoding(String encodedPassword)

Returns true if the encoded password should be encoded again for better security, else false. The default implementation always returns false.

翻译

如果为了更好的安全性,应再次对编码的密码进行编码,则返回true,否则返回false。默认实现始终返回false。

设置登录的用户名和密码

设置用户名和密码一般有配置文件和配置类两种方法. 需要连接数据库时, 需要使用配置类

1
2
spring.security.user.name=username
spring.security.user.password=password

继承自org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter类的配置类,现在已弃用。现在推荐配置UserDetailsService的Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class Config {
@Bean
UserDetailsService userDetailsService() {
// 配置用户名和密码
InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
users.createUser(User.withUsername("user").password("password").roles("admin").build());
return users;
}

// 配置加密器
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance(); // 不加密. 加密器详情见 PasswordEncoder 子类, 常用 BC 加密器
}
}

访问权限配置

配置SecurityFilterChain的Bean, 若配置发生冲突, 则以先匹配到的规则为准:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
public class Config() {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.formLogin() // 登录设置
.loginPage("/common/login") // 配置登录页面路径
.loginProcessingUrl("/login") // 登录数据处理路径
.defaultSuccessUrl("/common/index") // 登录成功后跳转路径

.and().logout().logoutUrl("/destroy") // 设置注销接口路径
.logoutSuccessUrl("/common/index")// 设置注销成功后跳转页面
.permitAll()

.and().authorizeRequests() //配置页面访问权限
.antMatchers("/common/index", "/common/login").permitAll() // 设置可直接访问的路径

.antMatchers("/authority/boom").hasAuthority("boom") //根据权限配置可访问路径
.antMatchers("/authority/read").hasAuthority("read")
.antMatchers("/authority/write").hasAuthority("write")
.antMatchers("/authority/read/write").hasAnyAuthority("read", "write")

.antMatchers("/role/boss").hasRole("boss") //根据角色配置可访问路径
.antMatchers("/role/worker").hasAuthority("ROLE_worker")
.antMatchers("/role/root").hasRole("root")
.antMatchers("/role/boss/worker").hasAnyRole("boss", "worker")

.antMatchers("/ip/localhost").hasIpAddress("192.168.0.0/16") //根据IP配置可访问路径
.antMatchers("/ip/localhost").hasIpAddress("127.0.0.0/8")

.anyRequest().authenticated() // 其他所有页面皆为认证用户访问

.and().exceptionHandling().accessDeniedPage("/error/403.html")// 配置 403 路径

.and().csrf().disable() // 关闭CSRF防护
.build();
}
}

登录页面

必须提交POST表单, 默认情况下, 用户名的name为username, 密码的name为password

注解

先开在启动类开启 @EnableGlobalMethodSecurity

  • @Secured
    先开在启动类开启 @EnableGlobalMethodSecurity(securedEnabled = true),对角色进行访问控制, 使用时对角色前加ROLE_前缀, 不支持对资源进行访问控制
    例如
    1
    @Secured({"ROLE_worker", "ROLE_boss"})
  • @PreAuthorize
    先开在启动类开启 @EnableGlobalMethodSecurity(prePostEnabled=true),提供了基于表达式的访问控制
    例如:
    1
    2
    @PreAuthorize("hasAnyRole('worker','boss')")
    @PreAuthorize("hasRole('worker') AND hasRole('boss')")
  • @PostAuthorize:
    先开在启动类开启 @EnableGlobalMethodSecurity(prePostEnabled=true),在方法执行后再进行权限验证, 适合验证带有返回值的权限, Spring EL 提供 返回对象能够在表达式语言中获取返回的对象returnObject, 提供了基于表达式的访问控制
    例如
    1
    @PostAuthorize(" returnObject != null &&  returnObject.username == authentication.name")
  • @PostFilter
    对返回数据过滤
    例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class controllerTest() {
@GetMapping("/post/filter")
@PostFilter("filterObject.username == '1'")
public List<User> PostFilter() {
List<User> userList = new ArrayList<>();
userList.add(new User().setUsername("1"));
userList.add(new User().setUsername("2"));
return userList;

// 返回数据
// [{
// "deleteFlag":null,
// "lockFlag":null,
// "userId":null,
// "username":"1",
// "password":null,
// "authorities":null,
// "enabled":false,
// "credentialsNonExpired":false,
// "accountNonExpired":false,
// "accountNonLocked":false
// }]
}
}
  • @PreFilter
    对传入参数过滤
    例如
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import org.springframework.security.access.prepost.PostFilter;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;

    @Controller
    public class controllerTest() {
    @PostMapping("/pre/filter")
    @PostFilter("filterObject.username == '1'")
    public List<User> PreFilter(@RequestBody List<User> userList){
    // 传入数据
    //[{
    // "username": "1"
    //},{
    // "username": "2"
    //},{
    // "username": "3"
    //}]
    return userList;

    // 返回数据
    //[
    // {
    // "deleteFlag": null,
    // "lockFlag": null,
    // "userId": null,
    // "username": "1",
    // "password": null,
    // "authorities": null,
    // "enabled": false,
    // "credentialsNonExpired": false,
    // "accountNonExpired": false,
    // "accountNonLocked": false
    // }
    //]
    }
    }

自动登录

配置自动登录Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
public class Config() {
@Bean
PersistentTokenRepository persistentTokenRepository(DataSource dataSource) {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
// jdbcTokenRepository.setCreateTableOnStartup(true); // 自动创建所需要的表
return jdbcTokenRepository;
}

@Bean
SecurityFilterChain filterChain(HttpSecurity http,
PersistentTokenRepository persistentTokenRepository,
UserDetailsService userDetailsService) throws Exception {
return http.rememberMe().tokenRepository(persistentTokenRepository) // 配置自动登录
.tokenValiditySeconds(60)// 自动登录有效时常
.userDetailsService(userDetailsService);
}
}