目录

  • 1 认证流程
  • 2 loginPage方法
  • 3 usernameParameter、passwordParameter方法
  • 4 successForwardUrl、failureForwardUrl、defaultSuccessUrl方法

1 认证流程

Spring Security是如何完成身份认证的?

具体拦截在UsernamePasswordAuthenticationFilter->AbstractAuthenticationProcessingFilter拦截器中;
具体认证流程在ProviderManager->AuthenticationManager中

UsernamePasswordAuthenticationFilter类

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBeanimplements ApplicationEventPublisherAware, MessageSourceAware {.....省略public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;//判断是否需要进行验证,其实就是判断请求的路径是否是/login,如果不是/login,说明不是form表单登录请求,则不需要进行验证if (!requiresAuthentication(request, response)) {chain.doFilter(request, response);return;//如果不需要验证,则直接return,下面的代码逻辑都不会走了}if (logger.isDebugEnabled()) {logger.debug("Request is to process authentication");}Authentication authResult;try {//调用attemptAuthentication方法进行验证,并获得验证结果Authentication对象。authResult = attemptAuthentication(request, response);if (authResult == null) {return;}//这段代码主要是为了防止session劫持(session fixation attacks)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) {// 验证失败unsuccessfulAuthentication(request, response, failed);return;}if (continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}//验证成功successfulAuthentication(request, response, chain, authResult);}.....省略
}

当验证失败时,需要抛出AuthenticationException异常。具体来说,验证失败可能分为2种情况:

  1. 验证服务失败,例如我们从数据库中查询用户名和密码验证用户,但是数据库服务挂了,此时抛出InternalAuthenticationServiceException异常
  2. 验证参数失败,例如用户输入的用户名和密码错误,此时抛出AuthenticationException异常

InternalAuthenticationServiceException是AuthenticationException的子类,因此我们看到在上面的catch代码块中,先捕获前者,再捕获后者。
无论是哪一种情况,都会调用unsuccessfulAuthentication方法,此方法内部会跳转到我们定义的登录失败页面。
如果验证成功,会调用successfulAuthentication方法,默认情况下,这个方法内部会将用户登录信息放到Session中,然后跳转到我们定义的登录成功页面。

上述代码中,最重要的就是attemptAuthentication方法,这是一个抽象方法,UsernamePasswordAuthenticationFilter中进行了实现,以实现自己的验证逻辑,也就是前面我看到的从HttpServletRequest对象中获得用户名和密码,进行验证。

public class UsernamePasswordAuthenticationFilter extendsAbstractAuthenticationProcessingFilter {.....省略public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;private boolean postOnly = true;public UsernamePasswordAuthenticationFilter() {super(new AntPathRequestMatcher("/login", "POST"));//指定form表单的action属性值为/login,且提交方式必须为post}//登录表单提交时,此方法会被回调,对用户名和密码进行校验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);//内部调用request.getParameter(usernameParameter)获得用户名String password = obtainPassword(request);//内部调用request.getParameter(passwordParameter)获得密码if (username == null) {username = "";}if (password == null) {password = "";}username = username.trim();// 用户名和密码被过滤器获取到,封装成AuthenticationUsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);setDetails(request, authRequest);// AuthenticationManager 身份管理器负责验证这个Authenticationreturn this.getAuthenticationManager().authenticate(authRequest);}.....省略
}

在UsernamePasswordAuthenticationFilter中,指定了form表单的method属性必须为post,同时指定了form的action属性值默认/login。当用户表单提交时,attemptAuthentication方法会被回调,这个方法内部会通过HttpServletRequest.getParameter的方式,获得表单中填写的用户名和密码的值,封装成一个UsernamePasswordAuthenticationToken对象,然后利用ProviderManager类进行校验。

ProviderManager类

public class ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean {.....省略public Authentication authenticate(Authentication authentication)throws AuthenticationException {Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;Authentication result = null;Authentication parentResult = 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 {// 由具体的provider认证result = provider.authenticate(authentication);if (result != null) {copyDetails(authentication, result);break;}}catch (AccountStatusException e) {prepareException(e, authentication);throw e;}catch (InternalAuthenticationServiceException e) {prepareException(e, authentication);throw e;}catch (AuthenticationException e) {lastException = e;}}if (result == null && parent != null) {try {result = parentResult = parent.authenticate(authentication);}catch (ProviderNotFoundException e) {}catch (AuthenticationException e) {lastException = e;}}if (result != null) {if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {((CredentialsContainer) result).eraseCredentials();}if (parentResult == null) {eventPublisher.publishAuthenticationSuccess(result);}return result;}if (lastException == null) {lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() },"No AuthenticationProvider found for {0}"));}prepareException(lastException, authentication);throw lastException;}.....省略
}

初次接触Spring Security的朋友相信会被AuthenticationManager,ProviderManager ,AuthenticationProvider …这么多相似的Spring认证类搞得晕头转向,但只要稍微梳理一下就可以理解清楚它们的联系和设计者的用意。AuthenticationManager(接口)是认证相关的核心接口,也是发起认证的出发点,因为在实际需求中,我们可能会允许用户使用用户名+密码登录,同时允许用户使用邮箱+密码,手机号码+密码登录,甚至,可能允许用户使用指纹登录(还有这样的操作?没想到吧),所以说AuthenticationManager一般不直接认证,AuthenticationManager接口的常用实现类ProviderManager 内部会维护一个List列表,存放多种认证方式,实际上这是委托者模式的应用(Delegate)。也就是说,核心的认证入口始终只有一个:AuthenticationManager,不同的认证方式:用户名+密码(UsernamePasswordAuthenticationToken),邮箱+密码,手机号码+密码登录则对应了三个AuthenticationProvider。这样一来四不四就好理解多了?熟悉shiro的朋友可以把AuthenticationProvider理解成Realm。在默认策略下,只需要通过一个AuthenticationProvider的认证,即可被认为是登录成功。

具体流程总结

  1. 用户名和密码被过滤器获取到,封装成Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。
  2. AuthenticationManager 身份管理器负责验证这个Authentication
  3. 在AuthenticationManager 通过AuthenticationProvider认证
  4. 认证成功后,AuthenticationManager身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除)Authentication实例。
  5. SecurityContextHolder安全上下文容器将第3步填充了信息的Authentication,通过SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。

时序图

2 loginPage方法

FormLoginConfigurer的loginPage方法用于自定义登录页面。如果我们没有调用这个方法,spring security将会注册一个DefaultLoginPageGeneratingFilter,这个filter的generateLoginPageHtml方法会帮我们生成一个默认的登录页面,也就是我们前面章节看到的那样。在本节案例中,我们自定义了登录页面地址为/login.html,则DefaultLoginPageGeneratingFilter不会被注册。
FormLoginConfigurer继承了AbstractAuthenticationFilterConfigurer,事实上,loginPage方法定义在这个类中。AbstractAuthenticationFilterConfigurer中维护了一个customLoginPage字段,用于记录用户是否设置了自定义登录页面。

AbstractAuthenticationFilterConfigurer#loginPage

protected T loginPage(String loginPage) {setLoginPage(loginPage);//当spring security判断用户需要登录时,会跳转到loginPage指定的页面中updateAuthenticationDefaults();this.customLoginPage = true; //标记用户使用了自定义登录页面return getSelf();
}private void setLoginPage(String loginPage) {this.loginPage = loginPage;this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
}

而在FormLoginConfigurer初始化时,会根据customLoginPage的值判断是否注册DefaultLoginPageGeneratingFilter。参见:

FormLoginConfigurer#initDefaultLoginFilter

private void initDefaultLoginFilter(H http) {DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageGeneratingFilter.class);//如果没有自定义登录页面,则使用DefaultLoginPageGeneratingFilterif (loginPageGeneratingFilter != null && !isCustomLoginPage()) {loginPageGeneratingFilter.setFormLoginEnabled(true);loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());loginPageGeneratingFilter.setFailureUrl(getFailureUrl());loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());}
}

在前面的配置中,我们指定的loginPage是一个静态页面login.html,我们也可以定义一个Controller,返回一个ModelAndView对象跳转到登录页面,注意Controller中方法的@RequestMapping注解的值,需要和loginPage方法中的值相对应。

可以看到这里上面还创建了一个LoginUrlAuthenticationEntryPoint对象,事实上,跳转的逻辑就是在这个类中完成的,当访问时,没有进行认证会一次调用过滤器,到FilterSecurityInterceptor过滤器是会验证是否认证,没有则抛出异常,这时候会在ExceptionTranslationFilter类中处理---->跳转或重定向等操作。

LoginUrlAuthenticationEntryPoint#commence

//commence方法用户判断跳转到登录页面时,是使用重定向(redirect)的方式,还是使用转发(forward)的方式
public void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {String redirectUrl = null;if (useForward) {//使用转发的方法,用户浏览器地址url不会发生改变if (forceHttps && "http".equals(request.getScheme())) {// First redirect the current request to HTTPS.// When that request is received, the forward to the login page will be// used.redirectUrl = buildHttpsRedirectUrlForRequest(request);}if (redirectUrl == null) {String loginForm = determineUrlToUseForThisRequest(request, response,authException);if (logger.isDebugEnabled()) {logger.debug("Server side forward to: " + loginForm);}RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);dispatcher.forward(request, response);return;}}else {//使用重定向的方式,用户浏览器地址url发生改变// redirect to login page. Use https if forceHttps trueredirectUrl = buildRedirectUrlToLoginPage(request, response, authException);}redirectStrategy.sendRedirect(request, response, redirectUrl);}

而commence方法的调用是在ExceptionTranslationFilter#handleSpringSecurityException中进行的。spring security中的验证错误会统一放到ExceptionTranslationFilter处理,其handleSpringSecurityException中,会判断异常的类型,如果是AuthenticationException类型的异常,则会调用sendStartAuthentication方法,进行跳转。如下

protected void sendStartAuthentication(HttpServletRequest request,HttpServletResponse response, FilterChain chain,AuthenticationException reason) throws ServletException, IOException {// SEC-112: Clear the SecurityContextHolder's Authentication, as the// existing Authentication is no longer considered validSecurityContextHolder.getContext().setAuthentication(null);requestCache.saveRequest(request, response);logger.debug("Calling Authentication entry point.");authenticationEntryPoint.commence(request, response, reason);//进行页面跳转}

3 usernameParameter、passwordParameter方法

通过前面的分析,我们知道在UsernamePasswordAuthenticationFilter中,是通过HttpServletRequest的getParamter方法来获得用户性和密码参数值的。默认的参数名是"username"、“password”。要求登录页面的form表单中的参数名,与此必须匹配。

我们可以通过调用FormLoginConfigurer的相关方法,重新定义参数名,例如

formLogin().usernameParameter("user") //form表单密码参数名.passwordParameter("pwd")//form表单用户名参数名

此时login.html页面的form表单也要进行相应的修改,如:

 <input type="text" name="user" placeholder="请输入用户名"><input type="password"  name="pwd" placeholder="请输入密码">

4 successForwardUrl、failureForwardUrl、defaultSuccessUrl方法

FormLoginConfigurer的successForwardUrl、failureForwardUrl方法分别用于定义登录成功和失败的跳转地址。

.formLogin().successForwardUrl("/success.html").failureForwardUrl("/error.html")

这两个方法内部实现如下:

public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));return this;
}public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));return this;
}

可以看到,内部利用跳转路径url分别构建了ForwardAuthenticationSuccessHandler、ForwardAuthenticationFailureHandler,用于跳转。

其中successHandler和failureHandler方法都继承自父类AbstractAuthenticationFilterConfigurer,用于给父类中维护的successHandler、failureHandler字段赋值。

因此FormLoginConfigurer的successForwardUrl、failureForwardUrl方法实际上只是AbstractAuthenticationFilterConfigurer的successHandler、failureHandler方法的一种快捷方式而已,我们可以直接调用successHandler、failureHandler方法,来定义跳转方式

ForwardAuthenticationSuccessHandler和ForwardAuthenticationFailureHandler的实现类似,以前者为例,其源码如下:

public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {private final String forwardUrl;public ForwardAuthenticationSuccessHandler(String forwardUrl) {Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), "'"+ forwardUrl + "' is not a valid forward URL");this.forwardUrl = forwardUrl;}//登录成功时,通过调用此方法进行页面的跳转,forward方式public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)throws IOException, ServletException {request.getRequestDispatcher(forwardUrl).forward(request, response);}
}

可以看到这个方法里面就是利用request.getRequestDispatcher来进行转发。

回顾1 我们分析formLogin方法源码时,在AbstractAuthenticationProcessingFilter的doFilter方法中,验证成功或者失败分别会回调successfulAuthentication、unsuccessfulAuthentication方法。事实上ForwardAuthenticationSuccessHandler的onAuthenticationSuccess方法就是在AbstractAuthenticationProcessingFilter的successfulAuthentication中被回调的。

AbstractAuthenticationProcessingFilter#successfulAuthentication

//用户验证成功后,回调此方法   protected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response, FilterChain chain, Authentication authResult)throws IOException, ServletException {if (logger.isDebugEnabled()) {logger.debug("Authentication success. Updating SecurityContextHolder to contain: "+ authResult);}//1、记录用户登录成功信息,默认放入到Session中SecurityContextHolder.getContext().setAuthentication(authResult);//2、如果开启了自动登录(用于支持我们经常在各个网站登录页面上的"记住我"复选框)rememberMeServices.loginSuccess(request, response, authResult);//3、 发布用户登录成功事件,我们可以定义一个bean,实现spring的ApplicationListener接口,则可以获取到所有的用户登录事件if (this.eventPublisher != null) {eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));}//4、最后调用ForwardAuthenticationSuccessHandler的onAuthenticationSuccess方法,进行页面的转发successHandler.onAuthenticationSuccess(request, response, authResult);}

defaultSuccessUrl
如果这样定义form表单登录,如果没有访问受保护的资源,登录成功后,默认跳转到的页面,默认值为"/"

protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/index.html").permitAll()//访问index.html不要权限验证.anyRequest().authenticated()//其他所有路径都需要权限校验.and().csrf().disable()//默认开启,这里先显式关闭.formLogin()  //内部注册 UsernamePasswordAuthenticationFilter.loginPage("/login.html") //表单登录页面地址.loginProcessingUrl("/login")//form表单POST请求url提交地址,默认为/login.passwordParameter("password")//form表单用户名参数名.usernameParameter("username")//form表单密码参数名}

我们可以通过配置defaultSuccessUrl来修改默认跳转到的页面

.formLogin().defaultSuccessUrl("/index.html")//如果没有访问受保护的资源,登录成功后,默认跳转到的页面,默认值为"/"

这里需要注意的是,在登录失败后,浏览器的地址会变为http://localhost:8080/login?error,也就是说,在loginProccessUrl方法指定的url后面加上?error。

由于这里使用的静态页面,所以无法展示错误信息。事实上,spring security会将错误信息放到Session中,key为SPRING_SECURITY_LAST_EXCEPTION,如果你使用jsp或者其他方式,则可以从session中把错误信息获取出来,常见的错误信息包括:

  • 用户名不存在:UsernameNotFoundException;
  • 密码错误:BadCredentialException;
  • 帐户被锁:LockedException;
  • 帐户未启动:DisabledException;
  • 密码过期:CredentialExpiredException;等等!

现在我们来分析,spring security是如何做到这种登录成功/失败跳转的挑战逻辑的:

登录成功

在AbstractAuthenticationFilterConfigurer中,默认的successHandler是SavedRequestAwareAuthenticationSuccessHandler。当访问受保护的目标页面,登录后直接跳转到目标页面,以及直接访问登录页面,登录后默认跳转到首页,就是通过SavedRequestAwareAuthenticationSuccessHandler这个类完成的。

public abstract class AbstractAuthenticationFilterConfigurer...{....private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
}

由于前面的案例中,我们调用了successForwardUrl方法,因此successHandler的默认值被覆盖为ForwardAuthenticationSuccessHandler,因此失去了这个功能。

具体来说,SavedRequestAwareAuthenticationSuccessHandler有一个RequestCache对象,当用户访问受保护的页面时,spring security会将当前请求HttpServletRequest对象信息放到这个RequestCache中。

参见ExceptionTranslationFilter#sendStartAuthentication方法

//需要进行登录验证
protected void sendStartAuthentication(HttpServletRequest request,HttpServletResponse response, FilterChain chain,AuthenticationException reason) throws ServletException, IOException {SecurityContextHolder.getContext().setAuthentication(null);//缓存当前request对象,其中包含了用户想访问的目标页面登录信息requestCache.saveRequest(request, response);logger.debug("Calling Authentication entry point.");//跳转到登录页面,这个之前已经分析过,不再赘述authenticationEntryPoint.commence(request, response, reason);}

当用户登录成功后,AbstractAuthenticationProcessingFilter#successfulAuthentication方法会被回调,这个方法源码之前也已经分析过,最后一步是调用

successHandler.onAuthenticationSuccess(request, response, authResult);

此时就是调用SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess

public class SavedRequestAwareAuthenticationSuccessHandler extendsSimpleUrlAuthenticationSuccessHandler {protected final Log logger = LogFactory.getLog(this.getClass());private RequestCache requestCache = new HttpSessionRequestCache();//这是一个session级别的缓存@Overridepublic void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response, Authentication authentication)throws ServletException, IOException {//1、从requestCache中获得之前保存的HttpServletRequest对象,SavedRequest是对HttpServletRequest的封装SavedRequest savedRequest = requestCache.getRequest(request, response);//2、如果用户直接访问的登录页面,则savedRequest为空,跳转到默认页面if (savedRequest == null) {super.onAuthenticationSuccess(request, response, authentication);return;}/*3 如果设置为targetUrlParameter参数,会从当前请求request对象,查看请求url是否包含要跳转到的路径参数,如果有则跳转到这个url,这个逻辑是在父类SimpleUrlAuthenticationSuccessHandler中进行的*/String targetUrlParameter = getTargetUrlParameter();if (isAlwaysUseDefaultTargetUrl()|| (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {//从requestCache中移除之前保存的request,以免缓存过多,内存溢出。//注意保存的是前一个request,移除的却是当前request,因为二者引用的是同一个session,内部只要找到这个session,移除对应的缓存key即可requestCache.removeRequest(request, response);super.onAuthenticationSuccess(request, response, authentication);return;}//4、移除在session中缓存的之前的登录错误信息,key为:SPRING_SECURITY_LAST_EXCEPTIONclearAuthenticationAttributes(request);//5、跳转到之前保存的request对象中访问的urlString targetUrl = savedRequest.getRedirectUrl();logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);getRedirectStrategy().sendRedirect(request, response, targetUrl);}public void setRequestCache(RequestCache requestCache) {this.requestCache = requestCache;}
}

登录失败

在AbstractAuthenticationFilterConfigurer中,failureHandler字段值默认为空,在初始化时,updateAuthenticationDefaults方法会被调用:

AbstractAuthenticationFilterConfigurer#updateAuthenticationDefaults

....
private AuthenticationFailureHandler failureHandler;
....
private void updateAuthenticationDefaults() {....if (failureHandler == null) {failureUrl(loginPage + "?error");}....}

可以看到,如果发现failureHandler为空,则会调用failureUrl方法创建一个AuthenticationFailureHandler实例,传入的参数是是我们设置的loginPage+"?ERROR",这也是我们在前面的gif动态图中,看到登录失败之后,登录页面变为http://localhost:8080/login?error的原因。

failUrl方法如下所示:

public final T failureUrl(String authenticationFailureUrl) {T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));this.failureUrl = authenticationFailureUrl;return result;}

可以看到这里创建的AuthenticationFailureHandler实现类为SimpleUrlAuthenticationFailureHandler。

Spinrg Security Authentication(一)相关推荐

  1. Phoenix 5.0 hbase 2.0 org.apache.hadoop.security.authentication.util.KerberosUtil.hasKerberosKeyTab

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  2. Spring Security 3.1 自定义 authentication provider

    来源:http://www.xeclipse.com/?p=1359 前言 在使用Spring Security的时候,遇到一个比较特殊的情况,需要根据用户名.邮箱等多个条件去验证用户或者使用第三方的 ...

  3. Spring Security小教程 Vol 2. Authentication核心组件介绍

    前言 上一期我们介绍了如何最简单的为一个SpringBoot应用添加Spring Security框架,并使其为应用完成用户鉴权和访问控制的授权服务. 这一期我们将聚焦在用户鉴权部分,用户鉴权又可以从 ...

  4. Spring Security:身份验证令牌Authentication介绍与Debug分析

    在Spring Security中,通过Authentication来封装用户的验证请求信息,Authentication可以是需要验证和已验证的用户请求信息封装.接下来,博主介绍Authentica ...

  5. 【Spring Security】五、自定义过滤器

    在之前的几篇security教程中,资源和所对应的权限都是在xml中进行配置的,也就在http标签中配置intercept-url,试想要是配置的对象不多,那还好,但是平常实际开发中都往往是非常多的资 ...

  6. Spring Security 进阶干货:自定义配置类入口WebSecurityConfigurerAdapter

    1. 前言 今天我们要进一步的的学习如何自定义配置 Spring Security 我们已经多次提到了 WebSecurityConfigurerAdapter ,而且我们知道 Spring Boot ...

  7. Spring Security 3.1 中功能强大的加密工具 PasswordEncoder

    去年发生的密码泄漏事件,我们也对密码加密做了重新研究.  在筛选加密方法的过程中,发现了Spring Security 3.1.0版本中提供了新的PasswordEncoder,它的加密方法非常给力! ...

  8. Spring Security 实战干货:从零手写一个验证码登录

    1. 前言 前面关于Spring Security胖哥又写了两篇文章,分别图文并茂地介绍了UsernamePasswordAuthenticationFilter和 AuthenticationMan ...

  9. Spring Security 实战:使用 JWT 认证访问接口

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 1. 前言 欢迎阅读Spring Security 实战 ...

最新文章

  1. 剑指offer:正则表达式匹配
  2. 陈老师Linux内核概述导学
  3. java jvm性能调优_java jvm性能优化
  4. android游戏开发框架libgdx的使用(六)--演员和演出
  5. Java代码示例: 使用reflections工具类获取某接口下所有的实现类
  6. hunnu---11547 你的组合数学学得如何?
  7. 【Linux笔记(000) 】-- 系统启动过程
  8. java ntlm_使用java UrlConnection使用ntlm(或kerberos)进行身份验证
  9. 【回归预测-ELM预测】基于樽海鞘算法结合极限学习机实现风电场功率回归预测附matlab代码
  10. 服务器系统tcpip.sys,win7系统tcpip.sys文件引起蓝屏的解决方法
  11. 《大学》全文及白话翻译
  12. 图片怎么改成jpg格式
  13. XBOX360游戏发售表(12月1日)
  14. js MessageBox 类 无插件,支持弹出层(原页面div 或者 div代码),消息框(alert confirm)
  15. 【Python】对英文文本进行词频统计(分词、字典排序、文件读写)
  16. 计算机科学与技术博士论文,计算机科学与技术一级学科博士研究生发表学术论文量化标准.pdf...
  17. 比尔盖茨The Best Books I Read in 2013
  18. 梦想实现_实现梦想的软件工程工作需要什么
  19. 如何将M4A格式音频转换成MP3格式?分享两个方法给大家!
  20. ZeppelinSpark Zeppelin开发spark

热门文章

  1. Maven精选系列(一):Maven私库搭建及使用
  2. 【 Node 】 ubuntu安装node版本
  3. 如何更好地进行游戏音乐制作
  4. npm install 报错 ‘proxy‘ config is set properly. See: ‘npm help config‘
  5. 短视频去水印的软件 视频消重软件吾爱伪原创
  6. 模拟和仿真的含义、各有什么特点、相互间的区别
  7. [SPI]SPI接口简介
  8. 端到端语音识别(二) ctc
  9. 网络营销:获取客户的有效方法
  10. HTML5系列代码:仅设置列的数目