- 外文名
- Security
- 包括了
- 网络通信安全
- 中文名
- 网络安全技术及其协议
- 推出了
- 各种网络和信息安全技术
-
spring security CSRF防护
2019-07-31 19:14:30从Spring Security 4.0开始,默认情况下会启用CSRF保护,以防止CSRF攻击应用程序,Spring Security CSRF会针对PATCH,POST,PUT和DELETE方法进行防护。 我这边是spring boot项目,在启用了@EnableWebSecurity...CSRF是指跨站请求伪造(Cross-site request forgery),是web常见的攻击之一。
从Spring Security 4.0开始,默认情况下会启用CSRF保护,以防止CSRF攻击应用程序,Spring Security CSRF会针对PATCH,POST,PUT和DELETE方法进行防护。
我这边是spring boot项目,在启用了@EnableWebSecurity注解后,csrf保护就自动生效了。
所以在默认配置下,即便已经登录了,页面中发起PATCH,POST,PUT和DELETE请求依然会被拒绝,并返回403,需要在请求接口的时候加入csrfToken才行。
如果你使用了freemarker之类的模板引擎或者jsp,针对表单提交,可以在表单中增加如下隐藏域:<input type = “hidden” name = “${_csrf.parameterName}” value = “${_csrf.token}” />
如果您使用的是JSON,则无法在HTTP参数中提交CSRF令牌。相反,您可以在HTTP头中提交令牌。一个典型的模式是将CSRF令牌包含在元标记中。下面显示了一个JSP示例:
<html> <head> <meta name = “_csrf” content = “${_csrf.token}” /> <!-- 默认标题名称是X-CSRF-TOKEN --> <meta name = “_csrf_header” content = “${_csrf.headerName}” /> </ head>
然后,您可以将令牌包含在所有Ajax请求中。如果您使用jQuery,可以使用以下方法完成此操作:
var token = $("meta[name='_csrf']").attr("content"); var header = $("meta[name='_csrf_header']").attr("content"); $.ajax({ url:url, type:'POST', async:false, dataType:'json', //返回的数据格式:json/xml/html/script/jsonp/text beforeSend: function(xhr) { xhr.setRequestHeader(header, token); //发送请求前将csrfToken设置到请求头中 }, success:function(data,textStatus,jqXHR){ } });
如果你不想启用CSRF保护,可以在spring security配置中取消csrf,如下:
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() ... http.csrf().disable(); //取消csrf防护 } }
-
spring security——基本介绍(一)
2019-01-16 10:31:23一、spring security 简介 spring security 的核心功能主要包括: 认证 (你是谁) 授权 (你能干什么) 攻击防护 (防止伪造身份) 其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic ...一、spring security 简介
spring security 的核心功能主要包括:
- 认证 (你是谁)
- 授权 (你能干什么)
- 攻击防护 (防止伪造身份)
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。
比如,对于username password认证过滤器来说,
会检查是否是一个登录请求;
是否包含username 和 password (也就是该过滤器需要的一些认证信息) ;
如果不满足则放行给下一个。
下一个按照自身职责判定是否是自身需要的信息,basic的特征就是在请求头中有 Authorization:Basic eHh4Onh4 的信息。中间可能还有更多的认证过滤器。最后一环是 FilterSecurityInterceptor,这里会判定该请求是否能进行访问rest服务,判断的依据是 BrowserSecurityConfig中的配置,如果被拒绝了就会抛出不同的异常(根据具体的原因)。Exception Translation Filter 会捕获抛出的错误,然后根据不同的认证方式进行信息的返回提示。
注意:绿色的过滤器可以配置是否生效,其他的都不能控制。
二、入门项目
首先创建spring boot项目HelloSecurity,其pom主要依赖如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies>
然后在src/main/resources/templates/目录下创建页面:
home.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example</title> </head> <body> <h1>Welcome!</h1> <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p> </body> </html>
我们可以看到, 在这个简单的视图中包含了一个链接: “/hello”. 链接到了如下的页面,Thymeleaf模板如下:
hello.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Hello World!</title> </head> <body> <h1>Hello world!</h1> </body> </html>
Web应用程序基于Spring MVC。 因此,你需要配置Spring MVC并设置视图控制器来暴露这些模板。 如下是一个典型的Spring MVC配置类。在src/main/java/hello目录下(所以java都在这里):
@Configuration public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/home").setViewName("home"); registry.addViewController("/").setViewName("home"); registry.addViewController("/hello").setViewName("hello"); registry.addViewController("/login").setViewName("login"); } }
addViewControllers()方法(覆盖WebMvcConfigurerAdapter中同名的方法)添加了四个视图控制器。 两个视图控制器引用名称为“home”的视图(在home.html中定义),另一个引用名为“hello”的视图(在hello.html中定义)。 第四个视图控制器引用另一个名为“login”的视图。 将在下一部分中创建该视图。此时,可以跳过来使应用程序可执行并运行应用程序,而无需登录任何内容。然后启动程序如下:
@SpringBootApplication public class Application { public static void main(String[] args) throws Throwable { SpringApplication.run(Application.class, args); } }
2、加入Spring Security
假设你希望防止未经授权的用户访问“/ hello”。 此时,如果用户点击主页上的链接,他们会看到问候语,请求被没有被拦截。 你需要添加一个障碍,使得用户在看到该页面之前登录。您可以通过在应用程序中配置Spring Security来实现。 如果Spring Security在类路径上,则Spring Boot会使用“Basic认证”来自动保护所有HTTP端点。 同时,你可以进一步自定义安全设置。首先在pom文件中引入:
<dependencies> ... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ... </dependencies>
如下是安全配置,使得只有认证过的用户才可以访问到问候页面:
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/home").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user").password("password").roles("USER"); } }
WebSecurityConfig类使用了@EnableWebSecurity注解 ,以启用Spring Security的Web安全支持,并提供Spring MVC集成。它还扩展了WebSecurityConfigurerAdapter,并覆盖了一些方法来设置Web安全配置的一些细节。
configure(HttpSecurity)方法定义了哪些URL路径应该被保护,哪些不应该。具体来说,“/”和“/ home”路径被配置为不需要任何身份验证。所有其他路径必须经过身份验证。
当用户成功登录时,它们将被重定向到先前请求的需要身份认证的页面。有一个由 loginPage()指定的自定义“/登录”页面,每个人都可以查看它。
对于configureGlobal(AuthenticationManagerBuilder) 方法,它将单个用户设置在内存中。该用户的用户名为“user”,密码为“password”,角色为“USER”。
现在我们需要创建登录页面。前面我们已经配置了“login”的视图控制器,因此现在只需要创建登录页面即可:
login.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example </title> </head> <body> <div th:if="${param.error}"> Invalid username and password. </div> <div th:if="${param.logout}"> You have been logged out. </div> <form th:action="@{/login}" method="post"> <div><label> User Name : <input type="text" name="username"/> </label></div> <div><label> Password: <input type="password" name="password"/> </label></div> <div><input type="submit" value="Sign In"/></div> </form> </body> </html>
你可以看到,这个Thymeleaf模板只是提供一个表单来获取用户名和密码,并将它们提交到“/ login”。 根据配置,Spring Security提供了一个拦截该请求并验证用户的过滤器。 如果用户未通过认证,该页面将重定向到“/ login?error”,并在页面显示相应的错误消息。 注销成功后,我们的应用程序将发送到“/ login?logout”,我们的页面显示相应的登出成功消息。最后,我们需要向用户提供一个显示当前用户名和登出的方法。 更新hello.html 向当前用户打印一句hello,并包含一个“注销”表单,如下所示:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Hello World!</title> </head> <body> <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1> <form th:action="@{/logout}" method="post"> <input type="submit" value="Sign Out"/> </form> </body> </html>
三、参数详解
1、注解 @EnableWebSecurity
在 Spring boot 应用中使用 Spring Security,用到了 @EnableWebSecurity注解,官方说明为,该注解和 @Configuration 注解一起使用, 注解 WebSecurityConfigurer 类型的类,或者利用@EnableWebSecurity 注解继承 WebSecurityConfigurerAdapter的类,这样就构成了 Spring Security 的配置。
2、抽象类 WebSecurityConfigurerAdapter
一般情况,会选择继承 WebSecurityConfigurerAdapter 类,其官方说明为:WebSecurityConfigurerAdapter 提供了一种便利的方式去创建 WebSecurityConfigurer的实例,只需要重写 WebSecurityConfigurerAdapter 的方法,即可配置拦截什么URL、设置什么权限等安全控制。
3、方法 configure(AuthenticationManagerBuilder auth) 和 configure(HttpSecurity http)
Demo 中重写了 WebSecurityConfigurerAdapter 的两个方法:
/** * 通过 {@link #authenticationManager()} 方法的默认实现尝试获取一个 {@link AuthenticationManager}. * 如果被复写, 应该使用{@link AuthenticationManagerBuilder} 来指定 {@link AuthenticationManager}. * * 例如, 可以使用以下配置在内存中进行注册公开内存的身份验证{@link UserDetailsService}: * * // 在内存中添加 user 和 admin 用户 * @Override * protected void configure(AuthenticationManagerBuilder auth) { * auth * .inMemoryAuthentication().withUser("user").password("password").roles("USER").and() * .withUser("admin").password("password").roles("USER", "ADMIN"); * } * * // 将 UserDetailsService 显示为 Bean * @Bean * @Override * public UserDetailsService userDetailsServiceBean() throws Exception { * return super.userDetailsServiceBean(); * } * */ protected void configure(AuthenticationManagerBuilder auth) throws Exception { this.disableLocalConfigureAuthenticationBldr = true; } /** * 复写这个方法来配置 {@link HttpSecurity}. * 通常,子类不能通过调用 super 来调用此方法,因为它可能会覆盖其配置。 默认配置为: * * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic(); * */ protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); }
4、final 类 HttpSecurity
HttpSecurity 常用方法及说明:
方法 说明 openidLogin()
用于基于 OpenId 的验证 headers()
将安全标头添加到响应 cors()
配置跨域资源共享( CORS ) sessionManagement()
允许配置会话管理 portMapper()
允许配置一个 PortMapper
(HttpSecurity#(getSharedObject(class))
),其他提供SecurityConfigurer
的对象使用PortMapper
从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl
映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443jee()
配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理 x509()
配置基于x509的认证 rememberMe
允许配置“记住我”的验证 authorizeRequests()
允许基于使用 HttpServletRequest
限制访问requestCache()
允许配置请求缓存 exceptionHandling()
允许配置错误处理 securityContext()
在 HttpServletRequests
之间的SecurityContextHolder
上设置SecurityContext
的管理。 当使用WebSecurityConfigurerAdapter
时,这将自动应用servletApi()
将 HttpServletRequest
方法与在其上找到的值集成到SecurityContext
中。 当使用WebSecurityConfigurerAdapter
时,这将自动应用csrf()
添加 CSRF 支持,使用 WebSecurityConfigurerAdapter
时,默认启用logout()
添加退出登录支持。当使用 WebSecurityConfigurerAdapter
时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()
身份验证,清除SecurityContextHolder
,然后重定向到”/login?success”anonymous()
允许配置匿名用户的表示方法。 当与 WebSecurityConfigurerAdapter
结合使用时,这将自动应用。 默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken
表示,并包含角色 “ROLE_ANONYMOUS”formLogin()
指定支持基于表单的身份验证。如果未指定 FormLoginConfigurer#loginPage(String)
,则将生成默认登录页面oauth2Login()
根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证 requiresChannel()
配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射 httpBasic()
配置 Http Basic 验证 addFilterAt()
在指定的Filter类的位置添加过滤器 5、类 AuthenticationManagerBuilder
/** * {@link SecurityBuilder} used to create an {@link AuthenticationManager}. Allows for * easily building in memory authentication, LDAP authentication, JDBC based * authentication, adding {@link UserDetailsService}, and adding * {@link AuthenticationProvider}'s. */
意思是,AuthenticationManagerBuilder 用于创建一个 AuthenticationManager,让我能够轻松的实现内存验证、LADP验证、基于JDBC的验证、添加UserDetailsService、添加AuthenticationProvider。
四、原理讲解
1、校验流程图
2、源码分析
- AbstractAuthenticationProcessingFilter 抽象类
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } if (logger.isDebugEnabled()) { logger.debug("Request is to process authentication"); } Authentication authResult; try { authResult = attemptAuthentication(request, response); if (authResult == null) { // return immediately as subclass has indicated that it hasn't completed // authentication return; } sessionStrategy.onAuthentication(authResult, request, response); } catch (InternalAuthenticationServiceException failed) { logger.error( "An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); return; } catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(request, response, failed); return; } // Authentication success if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } successfulAuthentication(request, response, chain, authResult); }
调用 requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。如果需要验证,则会调用 attemptAuthentication(HttpServletRequest, HttpServletResponse) 方法,有三种结果:
- 返回一个 Authentication 对象。配置的 SessionAuthenticationStrategy` 将被调用,然后 然后调用 successfulAuthentication(HttpServletRequest,HttpServletResponse,FilterChain,Authentication) 方法。
- 验证时发生 AuthenticationException。unsuccessfulAuthentication(HttpServletRequest, HttpServletResponse, AuthenticationException) 方法将被调用。
- 返回Null,表示身份验证不完整。假设子类做了一些必要的工作(如重定向)来继续处理验证,方法将立即返回。假设后一个请求将被这种方法接收,其中返回的Authentication对象不为空。
- UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter的子类)
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); String password = obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); }
attemptAuthentication () 方法将 request 中的 username 和 password 生成 UsernamePasswordAuthenticationToken 对象,用于 AuthenticationManager 的验证(即 this.getAuthenticationManager().authenticate(authRequest) )。默认情况下注入 Spring 容器的 AuthenticationManager 是 ProviderManager。
- ProviderManager(AuthenticationManager的实现类)
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled(); for (AuthenticationProvider provider : getProviders()) { if (!provider.supports(toTest)) { continue; } if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException e) { prepareException(e, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw e; } catch (InternalAuthenticationServiceException e) { prepareException(e, authentication); throw e; } catch (AuthenticationException e) { lastException = e; } } if (result == null && parent != null) { // Allow the parent to try. try { result = parent.authenticate(authentication); } catch (ProviderNotFoundException e) { // ignore as we will throw below if no other exception occurred prior to // calling parent and the parent // may throw ProviderNotFound even though a provider in the child already // handled the request } catch (AuthenticationException e) { lastException = e; } } if (result != null) { if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } eventPublisher.publishAuthenticationSuccess(result); return result; } // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(messages.getMessage( "ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } prepareException(lastException, authentication); throw lastException; }
尝试验证 Authentication 对象。AuthenticationProvider 列表将被连续尝试,直到 AuthenticationProvider 表示它能够认证传递的过来的Authentication 对象。然后将使用该 AuthenticationProvider 尝试身份验证。如果有多个 AuthenticationProvider 支持验证传递过来的Authentication 对象,那么由第一个来确定结果,覆盖早期支持AuthenticationProviders 所引发的任何可能的AuthenticationException。 成功验证后,将不会尝试后续的AuthenticationProvider。如果最后所有的 AuthenticationProviders 都没有成功验证 Authentication 对象,将抛出 AuthenticationException。从代码中不难看出,由 provider 来验证 authentication, 核心点方法是:
Authentication result = provider.authenticate(authentication);
此处的 provider 是 AbstractUserDetailsAuthenticationProvider,AbstractUserDetailsAuthenticationProvider 是AuthenticationProvider的实现,看看它的 authenticate(authentication) 方法:
// 验证 authentication public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); // Determine username String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { logger.debug("User '" + username + "' not found"); if (hideUserNotFoundExceptions) { throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { throw notFound; } } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { if (cacheWasUsed) { // There was a problem, so try again after checking // we're using latest data (i.e. not from the cache) cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } else { throw exception; } } postAuthenticationChecks.check(user); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } return createSuccessAuthentication(principalToReturn, authentication, user); }
AbstractUserDetailsAuthenticationProvider 内置了缓存机制,从缓存中获取不到的 UserDetails 信息的话,就调用如下方法获取用户信息,然后和 用户传来的信息进行对比来判断是否验证成功。
// 获取用户信息 UserDetails user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
retrieveUser() 方法在 DaoAuthenticationProvider 中实现,DaoAuthenticationProvider 是 AbstractUserDetailsAuthenticationProvider的子类。具体实现如下:
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { UserDetails loadedUser; try { loadedUser = this.getUserDetailsService().loadUserByUsername(username); } catch (UsernameNotFoundException notFound) { if (authentication.getCredentials() != null) { String presentedPassword = authentication.getCredentials().toString(); passwordEncoder.isPasswordValid(userNotFoundEncodedPassword, presentedPassword, null); } throw notFound; } catch (Exception repositoryProblem) { throw new InternalAuthenticationServiceException( repositoryProblem.getMessage(), repositoryProblem); } if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; }
可以看到此处的返回对象 userDetails 是由 UserDetailsService 的 #loadUserByUsername(username) 来获取的。
对于看本文比较困难的同学可以移步:https://www.lanqiao.cn/courses/3013
邀请码:STyLDQzM
其实内容和博客差不多,只不过更详细(就是一步一步的来,所有步骤有详细过程记录和截图,按照课程步骤最终能完整的操作完整个项目过程,适合小白),版本上也有所升级,还有就是有问题可以直接沟通,谢谢支持!
-
在Spring Boot中使用Spring Security实现权限控制
2017-01-12 15:52:37Spring Boot框架我们前面已经介绍了很多了,相信看了前面的博客的小伙伴对Spring Boot...OK,那我们今天要说的是Spring Boot中另外一个比较重要的东西,那就是Spring Security,这是一个专门针对基于Spring的项目的安全关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!
Spring Boot框架我们前面已经介绍了很多了,相信看了前面的博客的小伙伴对Spring Boot应该有一个大致的了解了吧,如果有小伙伴对Spring Boot尚不熟悉,可以先移步这里从SpringMVC到Spring Boot,老司机请略过。OK,那我们今天要说的是Spring Boot中另外一个比较重要的东西,那就是Spring Security,这是一个专门针对基于Spring的项目的安全框架,它主要是利用了我们前文介绍过的的AOP(Spring基础配置)来实现的。以前在Spring框架中使用Spring Security需要我们进行大量的XML配置,但是,Spring Boot在这里依然有惊喜带给我们,我们今天就一起来看看。
毫无疑问,Spring Boot针对Spring Security也提供了自动配置的功能,这些默认的自动配置极大的简化了我们的开发工作,我们今天就来看看这个吧。
#创建Project并添加相关依赖
Project的创建和前文一样,唯一要注意的地方就是创建的时候添加的依赖不同,如下图:
OK,创建成功之后添加相关依赖,数据库我这里使用MySql,所以添加MySql驱动,然后要添加Spring Security的支持,所以还要添加Spring Security的依赖,如下:<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency>
#配置application.properties
这个东东的配置还是和我们上文说到的是一样的,这里也没啥好说的,有问题的小伙伴翻看前文(初识在Spring Boot中使用JPA):spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/sang?useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=sang logging.level.org.springframework.security=info spring.thymeleaf.cache=false spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true
#定义用户和角色
我们这里使用JPA来定义用户和角色,用户和角色都存储在数据库中,我们直接通过在数据库中查询然后来使用。定义角色
我们的角色实体类和表都很简单,就两个字段,一个id,一个name属性表示角色的名称,实体类如下;
@Entity public class SysRole { @Id @GeneratedValue private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
OK,简简单单就这两个属性。
##定义用户
我们在定义用户的时候需要实现UserDetails接口,这样我们的用户实体即为Spring Security所使用的用户,定义好用户之后,我们还要配置用户和角色之间的多对多关系,正常情况下,角色和权限是两回事,所以我们还需要重写getAuthorities方法,将用户的角色和权限关联起来,代码如下:@Entity public class SysUser implements UserDetails { @Id @GeneratedValue private Long id; private String username; private String password; @ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER) private List<SysRole> roles; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public List<SysRole> getRoles() { return roles; } public void setRoles(List<SysRole> roles) { this.roles = roles; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> auths = new ArrayList<>(); List<SysRole> roles = this.getRoles(); for (SysRole role : roles) { auths.add(new SimpleGrantedAuthority(role.getName())); } return auths; } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
OK,经过上面两个步骤之后我们的用户就和角色关联起来了,这个时候运行Project就会在数据库中自动帮我们生成三张表,用户表、角色表和两者的关联表,如下:
##预设数据
我们先在表中定义好几个角色和用户,方便我们后边做测试用,OK,预设数据的话,那我们执行如下几行数据插入代码:insert into `sys_role`(`id`,`name`) values (1,'ROLE_ADMIN'),(2,'ROLE_USER'); insert into `sys_user`(`id`,`password`,`username`) values (1,'root','root'),(2,'sang','sang'); insert into `sys_user_roles`(`sys_user_id`,`roles_id`) values (1,1),(2,2);
我们向数据库中插入两个用户两个角色,再将这两个用户两个角色关联起来即可。
#创建传值对象
数据创建成功之后,在客户端请求网页的时候我们需要有一个实体类用来向客户端传递消息,OK,那我们创建一个MSG对象:public class Msg { private String title; private String content; private String extraInfo; public Msg() { } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getExtraInfo() { return extraInfo; } public void setExtraInfo(String extraInfo) { this.extraInfo = extraInfo; } public Msg(String title, String content, String extraInfo) { this.title = title; this.content = content; this.extraInfo = extraInfo; } }
这就是一个普通的类,没什么好说的。
#创建数据访问接口public interface SysUserRepository extends JpaRepository<SysUser, Long> { SysUser findByUsername(String username); }
这个也是写了n多遍的东西了,不赘述,关于这里如果小伙伴有疑问可以参考这里(初识在Spring Boot中使用JPA)。需要注意的是这里只需要一个根据用户名查询出用户的方法即可,不需要通过用户名和密码去查询。
#自定义UserDetailsService
自定义UserDetailsService,实现相应的接口,如下:public class CustomUserService implements UserDetailsService { @Autowired SysUserRepository userRepository; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { SysUser user = userRepository.findByUsername(s); if (user == null) { throw new UsernameNotFoundException("用户名不存在"); } System.out.println("s:"+s); System.out.println("username:"+user.getUsername()+";password:"+user.getPassword()); return user; } }
首先这里我们需要重写UserDetailsService接口,然后实现该接口中的loadUserByUsername方法,通过该方法查询到对应的用户,这里之所以要实现UserDetailsService接口,是因为在Spring Security中我们配置相关参数需要UserDetailsService类型的数据。
#SpringMVC配置@Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); } }
当用户访问login时跳转到login.html页面。
#配置Spring Security@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean UserDetailsService customUserService() { return new CustomUserService(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserService()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and().formLogin().loginPage("/login").failureUrl("/login?error").permitAll().and() .logout().permitAll(); } }
OK ,关于这个配置我要多说两句:
1.首先当我们要自定义Spring Security的时候我们需要继承自WebSecurityConfigurerAdapter来完成,相关配置重写对应 方法即可。
2.我们在这里注册CustomUserService的Bean,然后通过重写configure方法添加我们自定义的认证方式。
3.在configure(HttpSecurity http)方法中,我们设置了登录页面,而且登录页面任何人都可以访问,然后设置了登录失败地址,也设置了注销请求,注销请求也是任何人都可以访问的。
4.permitAll表示该请求任何人都可以访问,.anyRequest().authenticated()
,表示其他的请求都必须要有权限认证。
5.这里我们可以通过匹配器来匹配路径,比如antMatchers方法,假设我要管理员才可以访问admin文件夹下的内容,我可以这样来写:.antMatchers("/admin/**").hasRole("ROLE_ADMIN")
,也可以设置admin文件夹下的文件可以有多个角色来访问,写法如下:.antMatchers("/admin/**").hasAnyRole("ROLE_ADMIN","ROLE_USER")
6.可以通过hasIpAddress来指定某一个ip可以访问该资源,假设只允许访问ip为210.210.210.210的请求获取admin下的资源,写法如下.antMatchers("/admin/**").hasIpAddress("210.210.210.210")
7.更多的权限控制方式参看下表:
8.这里我们还可以做更多的配置,参考如下代码:http.authorizeRequests() .anyRequest().authenticated() .and().formLogin().loginPage("/login") //设置默认登录成功跳转页面 .defaultSuccessUrl("/index").failureUrl("/login?error").permitAll() .and() //开启cookie保存用户数据 .rememberMe() //设置cookie有效期 .tokenValiditySeconds(60 * 60 * 24 * 7) //设置cookie的私钥 .key("") .and() .logout() //默认注销行为为logout,可以通过下面的方式来修改 .logoutUrl("/custom-logout") //设置注销成功后跳转页面,默认是跳转到登录页面 .logoutSuccessUrl("") .permitAll();
OK,这里算是核心了,多说两句。
#创建登录页面
在template文件夹中创建login.html页面,内容如下:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>登录</title> <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/> <link rel="stylesheet" th:href="@{css/signin.css}"/> <style type="text/css"> body { padding-top: 50px; } .starter-template { padding: 40px 15px; text-align: center; } </style> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">Spring Security演示</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a th:href="@{/}">首页</a></li> <li><a th:href="@{http://www.baidu.com}">百度</a></li> </ul> </div> </div> </nav> <div class="container"> <div class="starter-template"> <p th:if="${param.logout}" class="bg-warning">已注销</p> <p th:if="${param.error}" class="bg-danger">有错误,请重试</p> <h2>使用账号密码登录</h2> <form class="form-signin" role="form" name="form" th:action="@{/login}" action="/login" method="post"> <div class="form-group"> <label for="username">账号</label> <input type="text" class="form-control" name="username" value="" placeholder="账号"/> </div> <div class="form-group"> <label for="password">密码</label> <input type="password" class="form-control" name="password" placeholder="密码"/> </div> <input type="submit" id="login" value="Login" class="btn btn-primary"/> </form> </div> </div> </body> </html>
这里就是一个普通的html页面,用到了thymeleaf模板引擎(thymeleaf可以参考这两篇文章使用Spring Boot开发Web项目/使用Spring Boot开发Web项目(二)之添加HTTPS支持),
#创建登录成功后跳转页面<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <head> <meta charset="UTF-8"/> <title sec:authentication="name"></title> <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/> <style type="text/css"> body { padding-top: 50px; } .starter-template { padding: 40px 15px; text-align: center; } </style> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">Spring Security演示</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a th:href="@{/}">首页</a></li> <li><a th:href="@{http://www.baidu.com}">百度</a></li> </ul> </div> </div> </nav> <div class="container"> <div class="starter-template"> <h1 th:text="${msg.title}"></h1> <p class="bg-primary" th:text="${msg.content}"></p> <div sec:authorize="hasRole('ROLE_ADMIN')"> <p class="bg-info" th:text="${msg.extraInfo}"></p> </div> <div sec:authorize="hasRole('ROLE_USER')"> <p class="bg-info">无更多显示信息</p> </div> <form th:action="@{/logout}" method="post"> <input type="submit" class="btn btn-primary" value="注销"/> </form> </div> </div> </body> </html>
这里有如下几个问题需要说明:
1.在html标签中我们引入的Spring Security
2.通过sec:authentication="name"我们可以获取当前用户名
3.sec:authorize="hasRole('ROLE_ADMIN')
表示当前用户角色为ROLE_ADMIN的话显示里边的内容
4.sec:authorize="hasRole('ROLE_USER')
表示当前用户角色为ROLE_USER的话显示该DIV里边的内容#添加控制器
@Controller public class HomeController { @RequestMapping("/") public String index(Model model) { Msg msg = new Msg("测试标题", "测试内容", "额外信息,只对管理员显示"); model.addAttribute("msg", msg); return "index"; } }
#测试
首页如下:
访问http://localhost:8080/自动跳转到http://localhost:8080/login
##登录出错
输入错误的账号密码进行登录,结果如下:
##管理员登录
使用管理员帐号密码登录,结果如下:
##普通用户登录
使用普通用户帐号密码登录,结果如下:
##注销
点击注销按钮,结果如下:OK,以上就是对Spring Security的一个简单介绍,是不是比自己通过过滤器、拦截器神马的来弄简单多了。
本文案例下载:
本文案例GitHub地址https://github.com/lenve/JavaEETest/tree/master/Test26-Security.更多Spring Boot资料请移步这里从SpringMVC到Spring Boot。
以上。
参考资料:
《JavaEE开发的颠覆者 Spring Boot实战》第九章 -
SpringSecurity与JWT整合
2020-12-06 21:14:33SpringSecurity与JWT整合 数据库基于:SpringSecurity从数据库中获取用户信息进行验证 依赖: <dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId...SpringSecurity与JWT整合
数据库基于:SpringSecurity从数据库中获取用户信息进行验证
依赖:
<dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
application.properties:
spring.thymeleaf.cache=false spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/springsecurity001?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource mybatis.mapper-locations=classpath:mapper/*.xml
启动类SecurityJwtApplication:
@SpringBootApplication @MapperScan("com.blu.mapper") public class SecurityJwtApplication { public static void main(String[] args) { SpringApplication.run(SecurityJwtApplication.class, args); } @Bean public BCryptPasswordEncoder bcryptPasswordEncoder(){ return new BCryptPasswordEncoder(); } }
MyUser:
@Data public class MyUser implements UserDetails { private Integer id; private String name; private String password; private List<MyRole> roles; @JsonIgnore @Override public Collection<? extends GrantedAuthority> getAuthorities() { return roles; } @JsonIgnore @Override public String getUsername() { return name; } @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } @JsonIgnore @Override public boolean isCredentialsNonExpired() { return true; } @JsonIgnore @Override public boolean isEnabled() { return true; } }
MyRole:
@Data public class MyRole implements GrantedAuthority { private Integer id; private String name; @JsonIgnore @Override public String getAuthority() { return name; } }
MyUserMapper:
public interface MyUserMapper { MyUser findByName(String name); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.blu.mapper.MyUserMapper"> <resultMap type="com.blu.entity.MyUser" id="myUserMap"> <id column="uid" property="id"></id> <result column="uname" property="name"></result> <result column="password" property="password"></result> <collection property="roles" ofType="com.blu.entity.MyRole"> <id column="rid" property="id" /> <result column="rname" property="name" /> </collection> </resultMap> <select id="findByName" parameterType="String" resultMap="myUserMap"> select u.id uid,u.name uname,u.password,r.id rid,r.name rname from user u,role r,role_user ur where u.name = #{name} and ur.user_id = u.id and ur.role_id = r.id </select> </mapper>
UserService:
public interface UserService extends UserDetailsService { }
UserServiceImpl:
@Service public class UserServiceImpl implements UserService { @Autowired private MyUserMapper myUserMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { MyUser myUser = myUserMapper.findByName(username); return myUser; } }
JwtLoginFilter:
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter { public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) { super(new AntPathRequestMatcher(defaultFilterProcessesUrl)); setAuthenticationManager(authenticationManager); } /** * 从登录参数json数据中获取用户名密码,然后调用AuthenticationManager.authenticate()方法进行校验。 */ @Override public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse resp) throws AuthenticationException, IOException, ServletException { //将用户传的json数据转为user对象 MyUser user = new ObjectMapper().readValue(req.getInputStream(),MyUser.class); return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword())); } /** * 校验成功 */ @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities(); StringBuffer sb = new StringBuffer(); for (GrantedAuthority authority : authorities) { sb.append(authority.getAuthority()).append(","); } String jwt = Jwts.builder() .claim("authorities", sb) .setSubject(authResult.getName()) .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000))//设置过期时间 .signWith(SignatureAlgorithm.HS512, "root@123")//设置加密方式,以及key .compact(); //设置登录成功后返回的信息 Map<String,String> map = new HashMap<>(); map.put("token",jwt); map.put("msg","登录成功"); response.setContentType("application/json;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write(new ObjectMapper().writeValueAsString(map)); writer.flush(); writer.close(); } /** * 校验失败 */ @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { Map<String,String> map = new HashMap<>(); map.put("msg","登录失败"); response.setContentType("application/json;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write(new ObjectMapper().writeValueAsString(map)); writer.flush(); writer.close(); } }
JwtFilter:
public class JwtFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; //从请求头中获取token String jwtToken = req.getHeader("authorization"); Jws<Claims> jws = Jwts.parser().setSigningKey("root@123") .parseClaimsJws(jwtToken.replace("Bearer", "")); Claims claims = jws.getBody(); String username = claims.getSubject(); //获取角色 List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("authorities")); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,"",authorities); SecurityContextHolder.getContext().setAuthentication(token); filterChain.doFilter(servletRequest,servletResponse); } }
测试接口:
@RestController public class StringController { @GetMapping("hello") public String hello() { return "hello BLU!"; } @GetMapping("hi") public String hi() { return "hi everyone!"; } @GetMapping("admin") public String admin() { return "hello admin!"; } }
Security配置类:
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private UserServiceImpl userServiceImpl; @Autowired private BCryptPasswordEncoder bcryptPasswordEncoder; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(HttpMethod.GET,"/hi").permitAll() .antMatchers("/hello").hasRole("vip1") .antMatchers("/admin").hasRole("admin"); http.formLogin().loginPage("/tologin") .usernameParameter("name") .passwordParameter("password") .loginProcessingUrl("/login"); http.csrf().disable(); http.addFilterBefore(new JwtLoginFilter("/login",authenticationManager()), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(new JwtFilter(),UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceImpl).passwordEncoder(bcryptPasswordEncoder); } }
测试错误密码登录:
测试正确密码登录:
测试访问(有权限):
测试访问(无权限):
测试访问(错误token):
-
-
SpringBoot集成Spring Security(1)——入门程序
2018-05-09 09:47:20因为项目需要,第一次接触Spring Security,早就听闻Spring Security强大但上手困难,今天学习了一天,翻遍了全网资料,才仅仅出入门道,特整理这篇文章来让后来者少踩一点坑(本文附带实例程序,请放心食用) ... -
springboot+security实现权限管理
2019-03-21 14:06:45Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,... -
springboot2.0+,整合security配置security关闭http基本验证
2019-01-12 15:14:34今天学习security,遇到了很多坑例如: spring boot1.5配置security关闭http基本验证,只需要在application.properites中配置 security.basic.enabled=false即可, 但是spring boot 2.0+之后这样配置就不能生效了。 ... -
Spring Security : 安全构建器HttpSecurity和WebSecurity的区别
2019-05-10 09:19:39Spring Security中,安全构建器HttpSecurity和WebSecurity的区别是 : WebSecurity不仅通过HttpSecurity定义某些请求的安全控制,也定义其他某些请求可以忽略安全控制; HttpSecurity仅用于定义需要安全控制的请求; ... -
SpringSecurity从数据库中获取用户信息进行验证
2020-12-05 22:17:23SpringSecurity从数据库中获取用户信息进行验证 基于 SpringBoot与SpringSecurity整合 案例的修改: 数据库 user 表 注,密码是由 BCrypt 算法加密对应用户名所得。 root $2a$10$uzHVooZlCWBkaGScKnpha.ZrK31NI... -
Spring Security 工作原理概览
2019-04-27 08:02:58本文由读者 muggle 投稿,muggle 是一位具备极客精神的90后单身老实猿,对 Spring Security 有丰富的使用经验,muggle 个人博客地址是 h... -
SpringSecurity
2020-12-13 21:56:20SpringSecurity(spring安全)SpringSecurity是用来干嘛的?Spring Security 核心功能Spring Security 工作流程1.导入POM坐标,thymeleaf用来测试Restful,security2.访问资源3.AOP横切进入,不用繁琐配置,来一个... -
Springboot集成SpringSecurity 附代码
2018-02-12 17:37:06一、Spring security 是什么? Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。 它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI... -
spring security中@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter四者的区别
2018-07-01 12:19:59spring security中可以通过表达式控制方法权限: Spring Security中定义了四个支持使用表达式的注解,分别是@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter。其中前两者可以用来在方法调用前或者调用后... -
基于Spring Security实现权限管理系统
2018-11-06 16:49:49基于Spring Security实现权限管理系统 稍微复杂一点的后台系统都会涉及到用户权限管理。何谓用户权限?我的理解就是,权限就是对数据(系统的实体类)和数据可进行的操作(增删查改)的集中管理。要构建一个可用的... -
深入浅出Spring Security(四):WebSecurity与HttpSecurity
2019-10-09 11:40:33前面我们已经分析了Spring Security的核心过滤器FilterChainProxy的创建和运行过程,认识了建造者和配置器的作用。 现在我们知道WebSecurity作为一个建造者就是用来创建核心过滤器FilterChainProxy实例的。 Web... -
springboot + spring security验证token进行用户认证
2018-11-23 15:40:56SecurityContextHolder是spring security最基本的组件。用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限等这些都被保存在SecurityContextHolder中。... -
Springboot + Spring Security 实现前后端分离登录认证及权限控制
2019-09-05 16:53:37Springboot + Spring Security 实现前后端分离登录认证及权限控制前言本文主要的功能文章目录一、数据库表设计建表语句初始化表数据语句二、Spring Security核心配置:WebSecurityConfig三、用户登录认证逻辑:... -
Cyber security和Network security的区别
2017-10-10 14:30:40主要是关于network security 和cyber security的区别。 -
Spring Security : HTTP请求安全构建器 HttpSecurity
2019-05-13 22:45:00HttpSecurity是Spring Security Config用于配置http请求安全控制的安全构建器(类似于Spring Security XML配置中的http命名空间配置部分),它的构建目标是一...该目标SecurityFilterChain最终会被Spring Security的安... -
SpringCloud——安全认证(Security)
2018-09-28 17:42:24一、SpringCloud Security简介 Spring Cloud Security提供了一组原语,用于构建安全的应用程序和服务,而且操作简便。可以在外部(或集中)进行大量配置的声明性模型有助于实现大型协作的远程组件系统,通常具有... -
Spring security 自定义过滤器实现Json参数传递-并兼容表单参数
2020-09-11 19:33:17依赖 <dependency> <groupId>org.springframework.boot</groupId>...spring-boot-starter-security</artifactId> </dependency> <dependency> <group. -
Springboot+Spring-Security+JWT 实现用户登录和权限认证
2019-06-08 22:26:17如今,互联网项目对于安全的要求越来越...Security,该框架提供了一整套比较成熟,也很完整的机制用于处理各类场景下的可以基于权限,资源路径,以及授权方面的解决方案,部分模块支持定制化,而且在和oauth2.0进... -
Shiro和Spring Security对比
2017-09-04 16:27:04目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底... -
SpringSecurity源码
2014-05-29 08:53:28SpringSecurity教程配套源码 专栏地址:http://blog.csdn.net/column/details/springsecurity.html -
Spring Security 入门
2019-09-16 13:10:22Spring Security4 -
SpringSecurity入门到入土教程_1
2020-12-30 21:45:59文章目录SpringSecurity 教程1. 简介1.1 概念1.2 入门案例1.3 自定义登录逻辑1.4 自定义登录页面1.5 自定用户名参数1.5 自定义成功处理器1.6 登录失败处理器1.7 认证anyRequestantMatchersregexMatchers1.8 授权基于... -
Cyber Security 和 Network Security的不同
2018-09-04 09:57:43网络范围区分: 1. Network Security: ... 主要是应对外部的威胁,network security 主要是应对内部网络安全,cyber security 主要是监控谁要翻越我们这座墙,或者在 我们没有充分照顾到的地... -
SpringBoot+SpringSecurity处理Ajax登录请求
2017-12-29 09:43:52最近在项目中遇到了这样一个问题:前后端分离,前端用Vue来做,所有的数据请求都使用vue-resource,没有使用表单,因此数据交互都是使用JSON,后台使用Spring Boot,权限验证使用了Spring Security,因为之前用... -
Spring Security文档
2019-01-30 12:57:041、项目 https://spring.io/projects/spring-security 2、文档 https://docs.spring.io/spring-security/site/docs/4.2.11.RELEASE/reference/htmlsingle/ 3、中文文档 ...
-
易语言开发通达信DLL公式接口
-
1120: 零起点学算法27——判断是否直角三角形
-
转行做IT-第5章 流程控制语句
-
python数据分析基础
-
小口径长焦透镜的焦距检测技术
-
机器人团队第四周学习总结
-
安川A1000系列说明书.rar
-
Leetcode 232. Implement Queue using Stacks
-
2020C-中小微企业的信贷决策.docx
-
开启或关闭vs code的代码悬浮框
-
浏览器本地存储
-
numpy常用方法(examples)
-
Appium自动化测试套餐
-
来了一波可恶的爬年终奖直接没了!
-
SQL专栏之常见注入备忘
-
Kotlin协程极简入门与解密
-
phalcon安装
-
Chapter 1 (Sample Space and Probability): Summary and Discussion
-
基于点扫描的同步辐射红外三维谱学显微研究
-
1119: 零起点学算法26——判断奇偶数