6.Spring security中的rememberMe
文章目录
- *RememberMe*
- *6.1RememberMe简介*
- *6.2RememberMe基本用法*
- *6.3持久化令牌*
- *6.4二次校验*
- *6.5原理分析*
- *`AbstractRememberMeServices`*
- *`TokenBasedRememberMeServices`*
- *`PersistentTokenBasedRememberMeServices`*
RememberMe
6.1RememberMe简介
RememberMe具体的实现思路就是通过cookie来记录当前用户身份。当用户登录成功之后,会通过一定的算法,将用户信息、时间戳等进行加密,加密完成后,通过响应头带回前端存储在cookie中,当浏览器关闭之后重新打开,如果再次访问该网站,会自动将cookie中的信息发送给服务器,服务器对cookie中的信息进行校验分析,进而确定出用户的身份,cookie中所保存的用户信息也是有时效的,例如三天、一周等。
由于相关信息存放在前端,因此存在着一定的安全隐患,不过可以通过持久化令牌以及二次校验来降低使用rememberMe所带来的安全风险。
6.2RememberMe基本用法
/*** 浏览器关闭或者服务器重启不需要重新登录。第一次进行表单登录时,多了一个请求参数remember-me: on,用来告诉服务端是否开启* RememberMe功能。* 如果自定义登录页面,那么默认情况下,是否开启RememberMe的参数就是remember-me。当请求成功后,在响应头中会多出一个* Set-Cookie,并携带remember-me字符串。以后所有请求的请求头Cookie字段,都会自动携带上这个令牌,服务端利用该令牌* 可以校验用户身份是否合法。*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@BeanPasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("javaboy").password("123").roles("admin");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().formLogin().and()// 最终会向过滤器链中添加RememberMeAuthenticationFilter过滤器.rememberMe().key("javaboy").and().csrf().disable();}
}
不过需要注意的是,这种方式隐患很大,一旦remember-me令牌泄露,恶意用户就可以拿着这个令牌去随意访问系统资源,持久化令牌和二次校验可以在一定程度上降低该问题带来的风险。
6.3持久化令牌
持久化令牌在普通令牌的基础上,新增了
series
和token
两个校验参数,当使用用户名/密码的方式登录时,series
才会自动更新;而一旦有了新的会话,token
就会重新生成。
所以,如果令牌被人盗用,一旦对方基于remember-me登录成功后,就会生成新的token
,自己的登录令牌就会失效,这样就能及时发现账户泄露并作出处理,比如清除自动登录令牌、通知用户账号泄露等。
Spring security中对于持久化令牌提供了两种实现:JdbcTokenRepositoryImpl
和InMemoryTokenRepositoryImpl
,前者是基于JdbcTemplate
来操作数据库,后者则是操作存储在内存中的数据,不过后者的使用场景很少,因此主要介绍前者的相关配置。
首先需要一张表来记录令牌信息,创建表的SQL脚本在JdbcTokenRepositoryImpl
类中的CREATE_TABLE_SQL
变量上已经定义好了:
public static final String CREATE_TABLE_SQL = "create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, "+ "token varchar(64) not null, last_used timestamp not null)";
接下来,引入JdbcTemplate和mysql依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
之后配置好数据库信息即可。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredDataSource dataSource;/*** 提供JdbcTokenRepositoryImpl实例,并配置数据源*/@BeanJdbcTokenRepositoryImpl jdbcTokenRepository() {JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);return jdbcTokenRepository;}@BeanPasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("javaboy").password("123").roles("admin");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().rememberMe()// 通过tokenRepository方法指定JdbcTokenRepositoryImpl实例.tokenRepository(jdbcTokenRepository()).and().csrf().disable();}
}
登录成功后,可以发现数据库表中多了一条记录。此时,如果关闭浏览器重新打开,再去访问
/hello
接口,访问时并不需要登录,但是访问成功之后,数据库中的token
字段会发生变化。同时,如果服务端重启之后,浏览器再去访问/hello
接口,依然不需要登录,但是token
字段也会更新,因为这两种情况中都有新会话的建立,而series
则不会更新。当然,如果用户注销登录,则数据库中和该用户相关的登录记录会自动清除。
6.4二次校验
二次校验就是将系统中的资源分为敏感的和不敏感的,如果用户使用了remember-me的方式登录,则访问敏感资源时会自动跳转到登录页面,要求用户重新登录;如果使用了用户名/密码的方式登录,则可以访问所有资源。
@RestController
public class HelloController {// 认证后可以访问,无论通过何种认证方式@GetMapping("/hello")public String hello() {return "hello";}// 必须通过用户名/密码方式认证后才可以访问@GetMapping("/admin")public String admin() {return "admin";}// 必须通过remember-me方式认证后才可以访问@GetMapping("/rememberme")public String rememberme() {return "rememberme";}
}@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {// 省略其他配置,同上@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 必须通过用户名/密码的方式认证后才可以访问.antMatchers("/admin").fullyAuthenticated()// 必须通过RememberMe的方式认证后才可以访问,至于/hello接口,认证后即可访问,无论通过何种认证方式.antMatchers("/rememberme").rememberMe().anyRequest().authenticated().and().formLogin().and().rememberMe().key("javaboy").tokenRepository(jdbcTokenRepository()).and().csrf().disable();}
}
6.5原理分析
从
RememberMeServices
接口开始介绍:
public interface RememberMeServices {// 从请求中提取出需要的参数,完成自动登录功能Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);// 自动登录失败的回调void loginFail(HttpServletRequest request, HttpServletResponse response);// 自动登录成功的回调void loginSuccess(HttpServletRequest request, HttpServletResponse response,Authentication successfulAuthentication);
}
NullRememberMeServices
是一个空的实现,不做讨论。
AbstractRememberMeServices
AbstractRememberMeServices
对于RememberMeServices
接口中定义的方法提供了基本的实现。
首先来看autoLogin
及其相关方法:
@Override
public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {// 从cookie中提取出remember-me对应的值String rememberMeCookie = extractRememberMeCookie(request);if (rememberMeCookie == null) {return null;}// 如果remember-me对应的值长度为0,则在返回null之前,执行cancelCookie,将remember-me值置为nullif (rememberMeCookie.length() == 0) {cancelCookie(request, response);return null;}try {// 对获取到的令牌进行解析,还原之后的字符串分为三部分,彼此之间用":"隔开,第一部分是当前登录用户名,// 第二部分是时间戳,第三部分是一个签名,而浏览器看到的是一个Base64编码后的字符串String[] cookieTokens = decodeCookie(rememberMeCookie);// 对cookie进行验证,如果验证通过,则返回登录用户对象,然后对用户状态进行校验(账户是否可用、是否锁定等)UserDetails user = processAutoLoginCookie(cookieTokens, request, response);this.userDetailsChecker.check(user);// 创建登录成功的用户对象,其类型是RememberMeAuthenticationTokenreturn createSuccessfulAuthentication(request, user);}catch (CookieTheftException ex) {cancelCookie(request, response);throw ex;}catch (UsernameNotFoundException ex) {this.logger.debug("Remember-me login was valid but corresponding user not found.", ex);}catch (InvalidCookieException ex) {this.logger.debug("Invalid remember-me cookie: " + ex.getMessage());}catch (AccountStatusException ex) {this.logger.debug("Invalid UserDetails: " + ex.getMessage());}catch (RememberMeAuthenticationException ex) {this.logger.debug(ex.getMessage());}cancelCookie(request, response);return null;
}/*** 提取出需要的cookie信息,即remember-me对应的值。如果这个值为null,表示本次请求携带的cookie中没有remember-me,* 这次不需要自动登录,直接返回null即可*/
protected String extractRememberMeCookie(HttpServletRequest request) {Cookie[] cookies = request.getCookies();if ((cookies == null) || (cookies.length == 0)) {return null;}for (Cookie cookie : cookies) {if (this.cookieName.equals(cookie.getName())) {return cookie.getValue();}}return null;
}protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {Cookie cookie = new Cookie(this.cookieName, null);cookie.setMaxAge(0);cookie.setPath(getCookiePath(request));if (this.cookieDomain != null) {cookie.setDomain(this.cookieDomain);}cookie.setSecure((this.useSecureCookie != null) ? this.useSecureCookie : request.isSecure());response.addCookie(cookie);
}protected Authentication createSuccessfulAuthentication(HttpServletRequest request, UserDetails user) {RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, user,this.authoritiesMapper.mapAuthorities(user.getAuthorities()));auth.setDetails(this.authenticationDetailsSource.buildDetails(request));return auth;
}
再来看下自动登录成功和自动登录失败的回调:
@Override
public final void loginFail(HttpServletRequest request, HttpServletResponse response) {// 取消cookie的设置cancelCookie(request, response);// 调用onLoginFail方法(空方法)onLoginFail(request, response);
}// 一般来说不需要重写
protected void onLoginFail(HttpServletRequest request, HttpServletResponse response) {}@Override
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response,Authentication successfulAuthentication) {// 判断当前请求是否开启了自动登录if (!rememberMeRequested(request, this.parameter)) {return;}onLoginSuccess(request, response, successfulAuthentication);
}protected abstract void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,Authentication successfulAuthentication);// 根据前端参数判断是否是remember-me请求,也可以在服务端配置,这样无论前端参数是什么,都会开启自动登录
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {if (this.alwaysRemember) {return true;}String paramValue = request.getParameter(parameter);if (paramValue != null) {if (paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")|| paramValue.equalsIgnoreCase("yes") || paramValue.equals("1")) {return true;}}return false;
}
最后再来看看
AbstractRememberMeServices
中一个比较重要的方法setCookie
,在自动登录成功后,将调用该方法把令牌信息放入响应头中并最终返回到前端:
protected void setCookie(String[] tokens, int maxAge, HttpServletRequest request, HttpServletResponse response) {String cookieValue = encodeCookie(tokens);// 配置cookieCookie cookie = new Cookie(this.cookieName, cookieValue);cookie.setMaxAge(maxAge);cookie.setPath(getCookiePath(request));if (this.cookieDomain != null) {cookie.setDomain(this.cookieDomain);}if (maxAge < 1) {cookie.setVersion(1);}cookie.setSecure((this.useSecureCookie != null) ? this.useSecureCookie : request.isSecure());cookie.setHttpOnly(true);// 将cookie对象放入到响应头中response.addCookie(cookie);
}// 将数组中的数据拼接成一个字符串并用":"隔开,然后对其进行Base64编码
protected String encodeCookie(String[] cookieTokens) {StringBuilder sb = new StringBuilder();for (int i = 0; i < cookieTokens.length; i++) {try {sb.append(URLEncoder.encode(cookieTokens[i], StandardCharsets.UTF_8.toString()));}catch (UnsupportedEncodingException ex) {this.logger.error(ex.getMessage(), ex);}if (i < cookieTokens.length - 1) {sb.append(DELIMITER);}}String value = sb.toString();sb = new StringBuilder(new String(Base64.getEncoder().encode(value.getBytes())));while (sb.charAt(sb.length() - 1) == '=') {sb.deleteCharAt(sb.length() - 1);}return sb.toString();
}
TokenBasedRememberMeServices
TokenBasedRememberMeServices
对AbstractRememberMeServices
中所定义的两个抽象方法processAutoLoginCookie
和onLoginSuccess
做出了相应的实现。
// 验证cookie中的令牌信息是否合法
@Override
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,HttpServletResponse response) {// 判断cookieTokens长度是否为3,不为3说明格式不对,则直接抛出异常if (cookieTokens.length != 3) {throw new InvalidCookieException("Cookie token did not contain 3" + " tokens, but contained '" + Arrays.asList(cookieTokens) + "'");}// 提取出过期时间,判断令牌是否过期,如果已经过期,则抛出异常long tokenExpiryTime = getTokenExpiryTime(cookieTokens);if (isTokenExpired(tokenExpiryTime)) {throw new InvalidCookieException("Cookie token[1] has expired (expired on '" + new Date(tokenExpiryTime)+ "'; current time is '" + new Date() + "')");}// 检查用户是否存在。将查找推迟到过期时间检查之后,从而可能避免昂贵的数据库调用UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);// 生成一个签名,将用户名、令牌过期时间、用户密码以及key组成一个字符串,中间用":"隔开,然后通过MD5消息摘要算法// 对该字符串进行加密String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails.getUsername(),userDetails.getPassword());// 判断生成的签名和通过cookie传来的签名是否相等,如果不相等则抛出异常if (!equals(expectedTokenSignature, cookieTokens[2])) {throw new InvalidCookieException("Cookie token[2] contained signature '" + cookieTokens[2]+ "' but expected '" + expectedTokenSignature + "'");}return userDetails;
}@Override
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,Authentication successfulAuthentication) {// 获取用户名和密码String username = retrieveUserName(successfulAuthentication);String password = retrievePassword(successfulAuthentication);// 如果找不到用户名和密码,只需中止,因为在这种情况下,TokenBasedMemberMeservices无法构造有效的令牌if (!StringUtils.hasLength(username)) {return;}if (!StringUtils.hasLength(password)) {UserDetails user = getUserDetailsService().loadUserByUsername(username);password = user.getPassword();if (!StringUtils.hasLength(password)) {return;}}// 计算令牌的过期时间,默认有效期是两周int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);long expiryTime = System.currentTimeMillis();// SEC-949expiryTime += 1000L * ((tokenLifetime < 0) ? TWO_WEEKS_S : tokenLifetime);// 根据令牌的过期时间、用户名、密码计算出一个签名String signatureValue = makeTokenSignature(expiryTime, username, password);// 设置cookiesetCookie(new String[] { username, Long.toString(expiryTime), signatureValue }, tokenLifetime, request,response);
}
- 当用户通过用户名/密码的形式登录成功后,系统会根据用户的用户名、密码以及令牌的过期时间计算出一个签名,这个签名使用MD5消息摘要算法生成,是不可逆的。
- 然后再将用户名、令牌过期时间以及签名拼接成一个字符串,中间用":"隔开,对拼接好的字符串进行Base64编码,然后将编码后的结果返回到前端,也就是在浏览器中看到的令牌。
- 当用户关闭浏览器再次打开,访问系统资源时会自动携带上cookie中的令牌,服务端拿到cookie中的令牌后,先进行Base64解码,解码后分别提取出令牌中的三项数据。
- 接着根据令牌中的数据判断令牌是否已经过期,如果没有过期,则根据令牌中的用户名查询出用户信息。
- 接着再计算出一个签名和令牌中的签名进行对比,如果一致,表示令牌是合法令牌,自动登录成功,否则自动登录失败。
PersistentTokenBasedRememberMeServices
在持久化令牌中,存储在数据库中的数据被封装成了一个对象
PersistentRememberMeToken
:
public class PersistentRememberMeToken {// 登录用户名private final String username;// 自动生成private final String series;// 自动生成private final String tokenValue;// 上次使用时间private final Date date;// 省略getter/setter
}
PersistentTokenBasedRememberMeServices
里边重要的方法也是processAutoLoginCookie
和onLoginSuccess
:
@Override
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,HttpServletResponse response) {// 第一项是series,第二项是tokenif (cookieTokens.length != 2) {throw new InvalidCookieException("Cookie token did not contain " + 2 + " tokens, but contained '"+ Arrays.asList(cookieTokens) + "'");}String presentedSeries = cookieTokens[0];String presentedToken = cookieTokens[1];// 根据series去数据库中查询出一个PersistentRememberMeToken对象PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);if (token == null) {// 没有series匹配,所以我们无法使用此cookie进行身份验证throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries);}// 如果token不相同,说明自动登录令牌已经泄露(恶意用户利用令牌登录后,数据库中的token发生变化了)if (!presentedToken.equals(token.getTokenValue())) {// 令牌和series值不匹配。删除该用户的所有登录信息,并抛出一个异常来警告他们this.tokenRepository.removeUserTokens(token.getUsername());throw new CookieTheftException(this.messages.getMessage("PersistentTokenBasedRememberMeServices.cookieStolen","Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));}// 判断token是否过期if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {throw new RememberMeAuthenticationException("Remember-me login has expired");}// token也匹配,所以登录是有效的。更新token值,保持相同的seriesPersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(),generateTokenData(), new Date());try {// 根据series去修改数据库中的token和datethis.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());addCookie(newToken, request, response);}catch (Exception ex) {throw new RememberMeAuthenticationException("Autologin failed due to data access problem");}return getUserDetailsService().loadUserByUsername(token.getUsername());
}private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {setCookie(new String[] { token.getSeries(), token.getTokenValue() }, getTokenValiditySeconds(), request,response);
}@Override
protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,Authentication successfulAuthentication) {String username = successfulAuthentication.getName();this.logger.debug(LogMessage.format("Creating new persistent login for user %s", username));PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, generateSeriesData(),generateTokenData(), new Date());try {this.tokenRepository.createNewToken(persistentToken);addCookie(persistentToken, request, response);}catch (Exception ex) {this.logger.error("Failed to save persistent token ", ex);}
}
PersistentTokenBasedRememberMeServices
和TokenBasedRememberMeServices
还是有一些明显的区别的:前者返回给前端的令牌是将series
和token
组成的字符串进行base64编码后返回给前端;后者返回给前端的令牌则是将用户名、过期时间以及签名组成的字符串进行base64编码后返回给前端。
那么,RememberMeServices
是在何时被调用的呢?
当开发者配置.rememberMe().key("javaboy")
时,实际上是引入了配置类RememberMeConfigurer
,其中最重要的就是init
和configure
方法:
@Override
public void init(H http) throws Exception {// 验证rememberMeServices和rememberMeCookieName没有同时设置validateInput();// 获取配置的key,如果没有配置,则会自动生成一个UUID字符串。如果开发者使用普通的remember-me,// 即没有使用持久化令牌,则建议自行配置该key,因为使用默认的UUID字符串,系统每次重启都会生成新的key,// 会导致之前下发的remember-me失效String key = getKey();// 如果开发者配置了tokenRepository,则获取到的实例是PersistentTokenBasedRememberMeServices,// 否则获取到TokenBasedRememberMeServicesRememberMeServices rememberMeServices = getRememberMeServices(http, key);http.setSharedObject(RememberMeServices.class, rememberMeServices);LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);if (logoutConfigurer != null && this.logoutHandler != null) {logoutConfigurer.addLogoutHandler(this.logoutHandler);}// 配置RememberMeAuthenticationProvider实例,其主要用来校验keyRememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(key);authenticationProvider = postProcess(authenticationProvider);http.authenticationProvider(authenticationProvider);initDefaultLoginFilter(http);
}@Override
public void configure(H http) {// 创建RememberMeAuthenticationFilter,同时传入RememberMeServices实例RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter(http.getSharedObject(AuthenticationManager.class), this.rememberMeServices);if (this.authenticationSuccessHandler != null) {rememberMeFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);}// 加入到spring容器中rememberMeFilter = postProcess(rememberMeFilter);// 加入到过滤器链中http.addFilter(rememberMeFilter);
}
再来看一下
RememberMeAuthenticationFilter
的doFilter
方法是如何运作的:
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {// 判断SecurityContextHolder中是否有值,没值的话表示用户尚未登录,此时调用autoLogin方法进行自动登录if (SecurityContextHolder.getContext().getAuthentication() != null) {chain.doFilter(request, response);return;}Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);// 不为空表示自动登录成功if (rememberMeAuth != null) {// 尝试通过AuthenticationManager进行身份验证try {// 调用RememberMeAuthenticationProvider的authenticate方法对key进行校验rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth);// 存储到SecurityContextHolder对象中SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);// 调用登录成功回调(空方法)onSuccessfulAuthentication(request, response, rememberMeAuth);if (this.eventPublisher != null) {// 发布登录成功事件this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(SecurityContextHolder.getContext().getAuthentication(), this.getClass()));}if (this.successHandler != null) {this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth);return;}} catch (AuthenticationException ex) {// 如果自动登录失败,则调用rememberMeServices.loginFail处理登录失败回调this.rememberMeServices.loginFail(request, response);// 空方法onUnsuccessfulAuthentication(request, response, ex);}}chain.doFilter(request, response);
}
这就是
RememberMeAuthenticationFilter
过滤器所做的事情,成功将RememberMeServices
的服务集成进来。
需要注意的是,RememberMeServices#loginSuccess
方法的调用位置,是在AbstractAuthenticationProcessingFilter#successfulAuthentication
中触发的,也就是说,无论是否开启了remember-me功能,该方法都会被调用。只不过在RememberMeServices#loginSuccess
方法的具体实现中,会去判断是否开启了remember-me,进而决定是否在响应中添加对应的cookie。
6.Spring security中的rememberMe相关推荐
- Spring Security 中取得 RememberMe 的 cookie 值
为什么80%的码农都做不了架构师?>>> Spring Security 中的 RememberMe 对应的 cookie 名称是可配置的--相信一般情况下大家也不会使用那个默 ...
- spring security 学习三-rememberMe
spring security 学习三-rememberMe 功能:登录时的"记住我"功能 原理: rememberMeAuthenticationFilter在security过 ...
- Spring Security中HttpSecurity常用方法及说明
本文来说下spring security中HttpSecurity常用方法,这个类在spring security中使用的非常多,功能十分丰富,其中包含的方法也是非常多,在实际的开发中,需要重写里面的 ...
- 【Spring Security】的RememberMe功能流程与源码详解
文章目录 前言 原理 基础版 搭建 初始化sql 依赖引入 配置类 验证 源码分析 进阶版 集成 源码分析 疑问1 疑问2 鉴权 升级版 集成 初始化sql 配置类 验证 源码分析 鉴权 流程 扩展版 ...
- Spring Security中文文档
Spring Security中文文档 来源:https://www.springcloud.cc/spring-security.html#overall-architecture 作者 Ben A ...
- Spring Security 中最流行的权限管理模型!
前面和大家说了 ACL,讲了理论,也给了一个完整的案例,相信小伙伴们对于 ACL 权限控制模型都已经比较了解了. 本文我要和大家聊一聊另外一个非常流行的权限管理模型,那就是 RBAC. 1.RBAC ...
- 一起搞清楚 Spring Security 中的 UserDetails
点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 170元买400元书的机会又来啦! 1. 前言 前一篇介绍了 Spring Security ...
- springsecurity sessionregistry session共享_要学就学透彻!Spring Security 中 CSRF 防御源码解析...
今日干货 刚刚发表查看:66666回复:666 公众号后台回复 ssm,免费获取松哥纯手敲的 SSM 框架学习干货. 上篇文章松哥和大家聊了什么是 CSRF 攻击,以及 CSRF 攻击要如何防御.主要 ...
- Http基本身份验证在Spring Security中如何工作?
在上一篇文章中,您学习了如何在基于Spring安全性的Java应用程序中启用Http基本身份验证 ,现在,我们将进一步进一步了解http基本身份验证在Spring安全性中的工作原理. 如果您还记得的话 ...
最新文章
- 不丢失精度的获取照片的Gps经纬度
- Ubuntu 13.10 安装软件失败后出现的问题——已安装 post-installation 脚本 返回了错误号 1...
- 在当当买了python怎么下载源代码-Python爬取当当网APP数据
- 剑指offer之 调整奇数偶数数组位置
- 【Iphone 游戏开发】游戏引擎剖析
- e7xue.php漏洞_简要分析最近的dedecms通杀漏洞以及漏洞补丁的绕过
- C语言嵌入系统编程修炼-性能优化
- java程序设计_Java程序设计--接口interface(笔记)
- 贺:MSN-.NET 技术交流群荣登群首页
- 全渠道数字化营销平台
- 手机微信html整人代码大全,2018年微信整人代码有哪些?2018年微信整人代码大全!...
- win10更新完提示未安装任何音频输出设备2019-11-13解决
- 机器学习项目汇总,值得收藏!
- XUL使用中的常见错误
- windows之在局域网内共享和共同编辑EXCEL
- 2019.7.13--jzDay9
- 客户端开发 Windows驱动开发(1)SDK WDK DDK WDM的关系
- mac电脑删除多余输入法
- 如何将pip更新到最新版本?
- android打开另外的app两种方式,内置到自己本身的app,重新打开app,
热门文章
- java画球_我的世界 如何用指令画球 JAVA 1.13+
- DNF域名解析全过程
- 在计算机海洋里摸爬滚打搜集的一些资源
- openlayers摸爬滚打 5.openlayers使用GeoJSON绘制点、线
- 2016鄂教版小学信息技术初识计算机软件,鄂教版(2016)五年级全册信息技术 25.揭秘计算机工作世界--初识计算机工作原理 教案...
- java实现第六届蓝桥杯立方体自身
- 科技计算机作文200字,关于网络的作文200字(5篇)
- 程序员的核心竞争力是什么?为什么?
- 荣耀magic3会用鸿蒙,荣耀magic3怎么样-荣耀magic3配置分析
- 1w存银行一年多少利息_五百万存银行 一年利息有多少呢?