spring security之httpSecurity 專題


 

37.5.2 Resolving the CsrfToken

Spring Security provides CsrfTokenArgumentResolver which can automatically resolve the current CsrfToken for Spring MVC arguments. By using @EnableWebSecurity you will automatically have this added to your Spring MVC configuration. If you use XML based configuraiton, you must add this yourself.

Once CsrfTokenArgumentResolver is properly configured, you can expose the CsrfToken to your static HTML based application.

@RestController
public class CsrfController {

    @RequestMapping("/csrf")
    public CsrfToken csrf(CsrfToken token) {
        return token;
    }
}

 

It is important to keep the CsrfToken a secret from other domains. This means if you are using Cross Origin Sharing (CORS), you should NOT expose the CsrfTokento any external domains.

https://docs.spring.io/spring-security/site/docs/4.2.3.RELEASE/reference/htmlsingle/#mvc-csrf

Include CSRF token in action

If allowing unauthorized users to upload temporariy files is not acceptable, an alternative is to place the MultipartFilter after the Spring Security filter and include the CSRF as a query parameter in the action attribute of the form. An example with a jsp is shown below

<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">

The disadvantage to this approach is that query parameters can be leaked. More genearlly, it is considered best practice to place sensitive data within the body or headers to ensure it is not leaked.
Additional information can be found in RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI’s.

https://docs.spring.io/spring-security/site/docs/4.2.3.RELEASE/reference/htmlsingle/#csrf-include-csrf-token-in-action

 

 

 

 

如果啟動csrf,Spring Security  /login和/logout的form中會自動集成csrf 

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>

 


運行時的html:
源代碼:

            <form name="form" th:action="@{/login}" action="/login" method="POST">
                <input type="text" id="username" class="span4" name="username" placeholder="Username"/> <br/>
                <input type="password" id="password" class="span4" name="password" placeholder="Password"/>
                <input type="checkbox" id="remember-me" th:class="checkbox" name="remember-me" checked="checked"/>
                <label for="remember-me" th:class="inline">Remember me</label>
                <button type="submit" name="submit" class="btn btn-info btn-block">Sign in</button>
            </form>

    <form th:action="@{/logout}" method="post">
        <input type="submit" value="Sign Out"/>
    </form>

 

 解決下面報錯的方案:
https://github.com/spring-projects/spring-security/issues/3906
As you suggested, the only thing you can really do to prevent this is to eagerly create the CsrfToken. In Spring Security 4.1.0.RELEASE+ and Java Configuration you can do this with:

http
        .csrf()
         .csrfTokenRepository(new HttpSessionCsrfTokenRepository())

This tells Spring Security that you do not want the token to be lazily persisted. If you are concerned with creating a session, you can also use the cookie implementation. For example:

http
    .csrf()
        .csrfTokenRepository(new CookieCsrfTokenRepository())

With Spring Boot, you can override the Spring Security version using the information from Customize dependency versions:

build.gradle

 ext['spring-security.version'] = '4.1.0.RELEASE'

pom.xml

<spring-security.version>4.1.0.RELEASE</spring-security.version>

 

 

with pages that do not require a login attempt to add a CSRF token, which is "too late" because some of the response has already been committed.
Suggested resolution: when using extras-springsecurity, (conditionally?) force generation of the session before starting to write to the buffer. (This is easily achievable using a Filter outside Thymeleaf but is somewhat tricky to identify)

Caused by: java.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:2886)
at org.apache.catalina.connector.Request.getSession(Request.java:2256)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:895)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:907)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
at org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.saveToken(HttpSessionCsrfTokenRepository.java:63)
at org.springframework.security.web.csrf.LazyCsrfTokenRepository$SaveOnAccessCsrfToken.saveTokenIfNecessary(LazyCsrfTokenRepository.java:176)
at org.springframework.security.web.csrf.LazyCsrfTokenRepository$SaveOnAccessCsrfToken.getToken(LazyCsrfTokenRepository.java:128)
at org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor.getExtraHiddenFields(CsrfRequestDataValueProcessor.java:71)
at org.thymeleaf.spring4.requestdata.RequestDataValueProcessor4Delegate.getExtraHiddenFields(RequestDataValueProcessor4Delegate.java:86)
at org.thymeleaf.spring4.requestdata.RequestDataValueProcessorUtils.getExtraHiddenFields(RequestDataValueProcessorUtils.java:155)
at org.thymeleaf.spring4.processor.SpringActionTagProcessor.doProcess(SpringActionTagProcessor.java:116)
at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:98)
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)

 

 

 

 

To configure the custom login form, create a @Configuration class by extending the WebSecurityConfigurerAdapter class as follows.

import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.inMemoryAuthentication()
      .withUser("admin").password("admin123").roles("USER");
   }

   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests().anyRequest().hasAnyRole("USER","ADMIN")
      .and()
      .authorizeRequests().antMatchers("/login**").permitAll()
      .and()
      .formLogin()
      .loginPage("/login") // Specifies the login page URL
      .loginProcessingUrl("/signin") // Specifies the URL,which is used 
                                     //in action attribute on the <from> tag
      .usernameParameter("userid")  // Username parameter, used in name attribute
                                    // of the <input> tag. Default is 'username'.
      .passwordParameter("passwd")  // Password parameter, used in name attribute
                                    // of the <input> tag. Default is 'password'.
      .successHandler((req,res,auth)->{    //Success handler invoked after successful authentication
         for (GrantedAuthority authority : auth.getAuthorities()) {
            System.out.println(authority.getAuthority());
         }
         System.out.println(auth.getName());
         res.sendRedirect("/"); // Redirect user to index/home page
      })
//    .defaultSuccessUrl("/")   // URL, where user will go after authenticating successfully.
                                // Skipped if successHandler() is used.
      .failureHandler((req,res,exp)->{  // Failure handler invoked after authentication failure
         String errMsg="";
         if(exp.getClass().isAssignableFrom(BadCredentialsException.class)){
            errMsg="Invalid username or password.";
         }else{
            errMsg="Unknown error - "+exp.getMessage();
         }
         req.getSession().setAttribute("message", errMsg);
         res.sendRedirect("/login"); // Redirect user to login page with error message.
      })
//    .failureUrl("/login?error")   // URL, where user will go after authentication failure.
                                    //  Skipped if failureHandler() is used.
      .permitAll() // Allow access to any URL associate to formLogin()
      .and()
      .logout()
      .logoutUrl("/signout")   // Specifies the logout URL, default URL is '/logout'
      .logoutSuccessHandler((req,res,auth)->{   // Logout handler called after successful logout 
         req.getSession().setAttribute("message", "You are logged out successfully.");
         res.sendRedirect("/login"); // Redirect user to login page with message.
      })
//    .logoutSuccessUrl("/login") // URL, where user will be redirect after successful
                                  //  logout. Ignored if logoutSuccessHandler() is used.
      .permitAll() // Allow access to any URL associate to logout()
      .and()
      .csrf().disable(); // Disable CSRF support
   }
}

https://www.boraji.com/spring-security-4-custom-login-from-example



 

 

 

 

org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer

public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<RememberMeConfigurer<H>, H> {
    /**
     * The default name for remember me parameter name and remember me cookie name
     */
    private static final String DEFAULT_REMEMBER_ME_NAME = "remember-me";

 


 

 

使用Spring-security默認的Form登陸頁:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .successForwardUrl("/home")
                .and()
                .authorizeRequests().anyRequest().permitAll();
    }
}

 

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class HomeController {

    @RequestMapping("/home")
    public ResponseEntity<String> home() {
        return ResponseEntity.ok("success");
    }

}

 訪問http://localhost:8080/home會跳轉到 http://localhost:8080/login輸入用戶名和密碼,則會跳轉到 http://localhost:8080/home

 

 

如果在HttpSecurity中配置需要authenticate(),則如果沒有登陸,或沒有相關權限,則會無法訪問

2017-01-02 23:39:32.027 DEBUG 10396 --- [nio-8080-exec-8] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/user'; against '/auth/**'
2017-01-02 23:39:32.028 DEBUG 10396 --- [nio-8080-exec-8] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /user; Attributes: [authenticated]
2017-01-02 23:39:32.029 DEBUG 10396 --- [nio-8080-exec-8] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: 
Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS 2017-01-02 23:39:32.030 DEBUG 10396 --- [nio-8080-exec-8] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@292b8b5a, returned: -1 2017-01-02 23:39:32.034 DEBUG 10396 --- [nio-8080-exec-8] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point org.springframework.security.access.AccessDeniedException: Access is denied at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEAS

 

 

配置session管理

下面的配置展示了只允許認證用戶在同一時間只有一個實例是如何配置的。若一個用戶使用用戶名為"user"認證並且沒有退出,同一個名為“user”的試圖再次認證時,第一個用戶的session將會強制銷毀,並設置到"/login?expired"的url。

      @Configuration
      @EnableWebSecurity
      public class SessionManagementSecurityConfig extends
              WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .anyRequest().hasRole("USER")
                      .and()
                 .formLogin()
                      .permitAll()
                      .and()
                 .sessionManagement()
 .maximumSessions(1) .expiredUrl("/login?expired");
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth.
                  inMemoryAuthentication()
                      .withUser("user")
                          .password("password")
                          .roles("USER");
          }
      }

當使用SessionManagementConfigurer的maximumSessio(int)時不用忘記為應用配置HttpSessionEventPublisher,這樣能保證過期的session能夠被清除。

在web.xml中可以這樣配置:

<listener>
   <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class></listener>

 

 

httpSecurity

   類似於spring security的xml配置文件命名空間配置中的<http>元素。它允許對特定的http請求基於安全考慮進行配置。默認情況下,適用於所有的請求,但可以使用requestMatcher(RequestMatcher)或者其它相似的方法進行限制。

使用示例:

最基本的基於表單的配置如下。該配置將所有的url訪問權限設定為角色名稱為"ROLE_USER".同時也定義了內存認證模式:使用用戶名"user"和密碼“password”,角色"ROLE_USER"來認證。

復制代碼
  @Configuration
  @EnableWebSecurity
  public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
 
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http
              .authorizeRequests()
                  .antMatchers("/").hasRole("USER")
                  .and()
              .formLogin();
      }
 
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth
               .inMemoryAuthentication()
                    .withUser("user")
                         .password("password")
                         .roles("USER");
      }
  }
復制代碼

 配置基於openId的認證方式

 basic示例,不使用attribute exchange

復制代碼
             @Configuration
      @EnableWebSecurity
      public class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .openidLogin()
                      .permitAll();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth
                      .inMemoryAuthentication()
                          // the username must match the OpenID of the user you are
                          // logging in with
                          .withUser("https://www.google.com/accounts/o8/id?id=lmkCn9xzPdsxVwG7pjYMuDgNNdASFmobNkcRPaWU")
                              .password("password")
                              .roles("USER");
          }
      }
復制代碼

下面展示一個更高級的示例,使用attribute exchange

復制代碼
      @Configuration
      @EnableWebSecurity
      public class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .openidLogin()
                      .loginPage("/login")
                      .permitAll()
                      .authenticationUserDetailsService(new AutoProvisioningUserDetailsService())
                          .attributeExchange("https://www.google.com/.")
                              .attribute("email")
                                  .type("http://axschema.org/contact/email")
                                  .required(true)
                                  .and()
                              .attribute("firstname")
                                  .type("http://axschema.org/namePerson/first")
                                  .required(true)
                                  .and()
                              .attribute("lastname")
                                  .type("http://axschema.org/namePerson/last")
                                  .required(true)
                                  .and()
                              .and()
                          .attributeExchange(".yahoo.com.")
                              .attribute("email")
                                  .type("http://schema.openid.net/contact/email")
                                  .required(true)
                                  .and()
                              .attribute("fullname")
                                  .type("http://axschema.org/namePerson")
                                  .required(true)
                                  .and()
                              .and()
                          .attributeExchange(".myopenid.com.")
                              .attribute("email")
                                  .type("http://schema.openid.net/contact/email")
                                  .required(true)
                                  .and()
                              .attribute("fullname")
                                  .type("http://schema.openid.net/namePerson")
                                  .required(true);
          }
      }
     
      public class AutoProvisioningUserDetailsService implements
              AuthenticationUserDetailsService&lt;OpenIDAuthenticationToken&gt; {
          public UserDetails loadUserDetails(OpenIDAuthenticationToken token) throws UsernameNotFoundException {
              return new User(token.getName(), "NOTUSED", AuthorityUtils.createAuthorityList("ROLE_USER"));
          }
      }
復制代碼

增加響應安全報文頭

默認情況下當使用WebSecuirtyConfigAdapter的默認構造函數時激活。

僅觸發Headers()方法而不觸發其它方法或者接受WebSecurityConfigureerAdater默認的,等同於:

      @Configuration
      @EnableWebSecurity
       public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .headers()
                      .contentTypeOptions();
                      .xssProtection()
                      .cacheControl()
                      .httpStrictTransportSecurity()
                      .frameOptions()
                      .and()
                  ...;
          }
      }

 

項目中用到iframe嵌入網頁,然后用到springsecurity就被攔截了 瀏覽器報錯"Refused to display 'http://......' in a frame because it set 'X-Frame-Options' to 'DENY'. "錯誤
原因是因為springSecurty使用X-Frame-Options防止網頁被Frame
解決辦法:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
 http.headers().frameOptions().disable();         http.csrf().disable();
        http.authorizeRequests().anyRequest().authenticated();

        http.formLogin()
                .defaultSuccessUrl("/platform/index", true)
                .loginPage("/login")
                .permitAll()
                .and()
                .logout();
        http.addFilterBefore(ssoFilterSecurityInterceptor(), FilterSecurityInterceptor.class);
    }

 

取消安全報文頭,如下:

      @Configuration
      @EnableWebSecurity
      public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .headers().disable()
                  ...;
          }
      }

使用部分安全報文頭

觸發headers()方法的返回結果,例如,只使用HeaderConfigurer的cacheControll()方法和HeadersConfigurer的frameOptions()方法.

      @Configuration
      @EnableWebSecurity
      public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .headers()
                      .cacheControl()
                      .frameOptions()
                      .and()
                  ...;
          }
      }

配置PortMapper

允許配置一個從HttpSecurity的getSharedObject(Class)方法中獲取的PortMapper。當http請求跳轉到https或者https請求跳轉到http請求時(例如我們和requiresChanenl一起使用時),別的提供的SecurityConfigurer對象使用P誒賬戶的PortMapper作為默認的PortMapper。默認情況下,spring security使用PortMapperImpl來映射http端口8080到https端口8443,並且將http端口的80映射到https的端口443.

配置示例如下,下面的配置將確保在spring security中的http請求端口9090跳轉到https端口9443 並且將http端口80跳轉到https443端口。

復制代碼
      @Configuration
      @EnableWebSecurity
      public class PortMapperSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .formLogin()
                      .permitAll()
                      .and()
                      // Example portMapper() configuration
                      .portMapper()
                          .http(9090).mapsTo(9443)
                          .http(80).mapsTo(443);
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth
                  .inMemoryAuthentication()
                      .withUser("user")
                          .password("password")
                          .roles("USER");
          }
      }
復制代碼

配置基於容器的預認證

在這個場景中,servlet容器管理認證。

配置示例:

下面的配置使用HttpServletRequest中的principal,若用戶的角色是“ROLE_USER”或者"ROLE_ADMIN",將會返回Authentication結果。

復制代碼
    @Configuration
      @EnableWebSecurity
      public class JeeSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  // Example jee() configuration
                  .jee()
                      .mappableRoles("ROLE_USER", "ROLE_ADMIN");
          }
      }
復制代碼

開發者希望使用基於容器預認證時,需要在web.xml中配置安全限制。例如:

復制代碼
 <login-config>
          <auth-method>FORM</auth-method>
          <form-login-config>
              <form-login-page>/login</form-login-page>
              <form-error-page>/login?error</form-error-page>
          </form-login-config>
      </login-config>
     
      <security-role>
          <role-name>ROLE_USER</role-name>
      </security-role>
      <security-constraint>
          <web-resource-collection>
          <web-resource-name>Public</web-resource-name>
              <description>Matches unconstrained pages</description>
              <url-pattern>/login</url-pattern>
              <url-pattern>/logout</url-pattern>
              <url-pattern>/resources/</url-pattern>
          </web-resource-collection>
      </security-constraint>
      <security-constraint>
          <web-resource-collection>
              <web-resource-name>Secured Areas</web-resource-name>
              <url-pattern>/</url-pattern>
          </web-resource-collection>
          <auth-constraint>
              <role-name>ROLE_USER</role-name>
          </auth-constraint>
      </security-constraint>
復制代碼

配置基於X509的預認證

配置示例,下面的配置試圖從X509證書中提取用戶名,注意,為完成這個工作,客戶端請求證書需要配置到servlet容器中。

復制代碼
      @Configuration
      @EnableWebSecurity
      public class X509SecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  // Example x509() configuration
                  .x509();
          }
      }
復制代碼

配置Remember-me服務

配置示例,下面的配置展示了如何允許基於token的remember-me的認證。若http參數中包含一個名為“remember-me”的參數,不管session是否過期,用戶記錄將會被記保存下來。

復制代碼
 @Configuration
      @EnableWebSecurity
      public class RememberMeSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER");
          }
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .formLogin()
                      .permitAll()
                      .and()
                   // Example Remember Me Configuration
                  .rememberMe();
          }
      }
復制代碼

限制HttpServletRequest的請求訪問

配置示例,最基本的示例是配置所有的url訪問都需要角色"ROLE_USER".下面的配置要求每一個url的訪問都需要認證,並且授權訪問權限給用戶"admin"和"user".

復制代碼
             @Configuration
      @EnableWebSecurity
      public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .formLogin();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER")
                             .and()
                        .withUser("adminr")
                             .password("password")
                             .roles("ADMIN","USER");
          }
      }
復制代碼

同樣,也可以配置多個url。下面的配置要求以/admin/開始的url訪問權限為“admin”用戶。

復制代碼
 @Configuration
      @EnableWebSecurity
      public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/admin/**").hasRole("ADMIN")
                      .antMatchers("/**").hasRole("USER")
                      .and()
                  .formLogin();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER")
                             .and()
                        .withUser("adminr")
                             .password("password")
                             .roles("ADMIN","USER");
          }
      }
復制代碼

注意:匹配起效是按照順序來的。因此如果下面的配置是無效的,因為滿足第一個規則后將不會檢查第二條規則:

      http
          .authorizeRequests()
              .antMatchers("/**").hasRole("USER")
              .antMatchers("/admin/**").hasRole("ADMIN")

增加CSRF支持

默認情況下,當使用WebSecurityConfigurerAdapter時的默認構造方法時CSRF是激活的。你可以使用如下方法關閉它:

復制代碼
      @Configuration
      @EnableWebSecurity
      public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .csrf().disable()
                  ...;
          }
      }
復制代碼

增加logout支持

默認支持,當使用WebSecurityConfigurerAdapter時Logout是支持的。當用戶發出“/logout”請求時,系統將會銷毀session並且清空配置的rememberMe()認證,然后清除SecurityContextHolder,最后跳向logout成功頁面或者登陸頁面。

復制代碼
 @Configuration
      @EnableWebSecurity
      public class LogoutSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .formLogin()
                      .and()
                  // sample logout customization
                  .logout()
 .deleteCookies("remove") .invalidateHttpSession(false) .logoutUrl("/custom-logout") .logoutSuccessUrl("/logout-success");
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER");
          }
      }
復制代碼

匿名用戶控制

使用WebSecurityConfigurerAdapter時自動綁定。默認情況下,匿名用戶有一個AnonymousAuthenticationToken標示,包含角色"ROLE_ANONYMOUS"。

下面的配置展示了如何指定匿名用戶應該包含"ROLE_ANON".

復制代碼
             @Configuration
      @EnableWebSecurity
      public class AnononymousSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .formLogin()
                      .and()
                  // sample anonymous customization
                  .anonymous()
                      .authorities("ROLE_ANON");
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER");
          }
      }
復制代碼

基於表單的認證

若FormLoginConfigurer的loginpage(String)沒有指定,將會產生一個默認的login頁面。

示例配置:

復制代碼
 @Configuration
      @EnableWebSecurity
      public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/**").hasRole("USER")
                      .and()
                  .formLogin();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER");
          }
      }
復制代碼

下面的示例展示了自定義的表單認證:

復制代碼
 @Configuration
      @EnableWebSecurity
      public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/").hasRole("USER")
                      .and()
                  .formLogin()
                         .usernameParameter("j_username") // default is username
                         .passwordParameter("j_password") // default is password
                         .loginPage("/authentication/login") // default is /login with an HTTP get
                         .failureUrl("/authentication/login?failed") // default is /login?error
                         .loginProcessingUrl("/authentication/login/process"); // default is /login with an HTTP post
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER");
          }
      }
復制代碼

配置安全通道

為使配置生效,需至少配置一個通道的映射。

配置示例:

下面例子展示了如何將每個請求都使用https通道。

復制代碼
 @Configuration
      @EnableWebSecurity
      public class ChannelSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/**").hasRole("USER")
                      .and()
                  .formLogin()
                      .and()
                  .channelSecurity()
                      .anyRequest().requiresSecure();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                   .inMemoryAuthentication()
                        .withUser("user")
                             .password("password")
                             .roles("USER");
          }
      }
復制代碼

配置http 基本認證

配置示例:

復制代碼
 @Configuration
      @EnableWebSecurity
      public class HttpBasicSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/**").hasRole("USER").and()
                      .httpBasic();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                  .inMemoryAuthentication()
                      .withUser("user")
                          .password("password")
                          .roles("USER");
          }
      }
復制代碼

配置要觸發的HttpRequest

重寫RequestMatcher方法、antMatcher()z、regexMatcher()等。

配置示例

下面的配置使HttpSecurity接收以"/api/","/oauth/"開頭請求。

復制代碼
 @Configuration
      @EnableWebSecurity
      public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .requestMatchers()
                      .antMatchers("/api/**","/oauth/**")
                      .and()
                  .authorizeRequests()
                      .antMatchers("/**").hasRole("USER").and()
                      .httpBasic();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                  .inMemoryAuthentication()
                      .withUser("user")
                          .password("password")
                          .roles("USER");
          }
      }
復制代碼

下面的配置和上面的相同:

復制代碼
 @Configuration
      @EnableWebSecurity
      public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .requestMatchers()
                      .antMatchers("/api/**")
                      .antMatchers("/oauth/**")
                      .and()
                  .authorizeRequests()
                      .antMatchers("/**").hasRole("USER").and()
                      .httpBasic();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                  .inMemoryAuthentication()
                      .withUser("user")
                          .password("password")
                          .roles("USER");
          }
      }
復制代碼

同樣也可以這樣使用:

復制代碼
@Configuration
      @EnableWebSecurity
      public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
     
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .requestMatchers()
                      .antMatchers("/api/**")
                      .and()
                  .requestMatchers()
                      .antMatchers("/oauth/**")
                      .and()
                  .authorizeRequests()
                      .antMatchers("/**").hasRole("USER").and()
                      .httpBasic();
          }
     
          @Override
          protected void configure(AuthenticationManagerBuilder auth)
                  throws Exception {
              auth
                  .inMemoryAuthentication()
                      .withUser("user")
                          .password("password")
                          .roles("USER");
          }
      }

 

復制代碼

小結:

   本文是從httpSecurity代碼中整理得來的,有助於對spring security的全面理解

http://www.cnblogs.com/davidwang456/p/4549344.html?utm_source=tuicool

 

前一節學習了如何限制登錄嘗試次數,今天在這個基礎上再增加一點新功能:Remember Me. 很多網站,比如博客園,在登錄頁面就有這個選項,勾選“下次自動登錄”后,在一定時間段內,只要不清空瀏覽器Cookie,就可以自動登錄。

一、spring-security.xml 最簡單的配置

1     <http auto-config="true" use-expressions="true">
2         ...
3         <remember-me />
4     </http>

即:在<http></http>節點之間,加一行<rember-me/>,然后

1  <authentication-manager erase-credentials="false">  
2       ...
3 </authentication-manager> 

 在<authentication-manager>節點增加一個屬性erase-credentials="false" ,配置的修改就算完了

二、登錄頁login.jsp

1 <input id="_spring_security_remember_me" name="_spring_security_remember_me" type="checkbox" value="true"/>

 加上這個checkbox勾選框即可

原理簡析:按上面的步驟修改后,如果在登錄時勾選了Remember Me,登錄成功后,會在瀏覽器中生成一個名為SPRING_SECURITY_REMEMBER_ME_COOKIE的Cookie項,默認有效值為2周,其值是一個加密字符串,其值據說與用戶名、密碼等敏感數據有關!

下次再進入該頁面時,Spring Security的springSecurityFilterChain這個Filter會檢測有沒有這個Cookie,如果有,就自動登錄。

三、安全性分析

安全性分析:這樣雖然很方便,但是大家都知道Cookie畢竟是保存在客戶端的,很容易盜取,而且cookie的值還與用戶名、密碼這些敏感數據相關,雖然加密了,但是將敏感信息存在客戶端,畢竟不太安全。

建議:對於一些重要操作,比如:電子商務中的在線支付、修改用戶密碼等需要本人親自操作的業務環節,還是要將用戶引導至登錄頁,重新登錄,以保證安全。為了達到這個目的,代碼就必須在jsp前端以java后端,有辦法檢測出當前登錄的用戶,是否通過“Remember Me Cookie”自動登錄,還是通過“輸入用戶名、密碼”安全登錄。

在jsp前端檢查是否Remember Me自動登錄很簡單,直接使用security提供的tag標簽即可,類似下面這樣:

1 <%@taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
2 ...
3 <sec:authorize access="isRememberMe()">
4         ...    
5 </sec:authorize>

在java 服務端的Controller中,可這樣檢測:

復制代碼
 1     /**
 2      * 判斷用戶是否從Remember Me Cookie自動登錄
 3      * @return
 4      */
 5     private boolean isRememberMeAuthenticated() {
 6 
 7         Authentication authentication = SecurityContextHolder.getContext()
 8                 .getAuthentication();
 9         if (authentication == null) {
10             return false;
11         }
12 
13         return RememberMeAuthenticationToken.class
14                 .isAssignableFrom(authentication.getClass());
15     }
復制代碼

此外,spring security還提供了remember me的另一種相對更安全的實現機制 :在客戶端的cookie中,僅保存一個無意義的加密串(與用戶名、密碼等敏感數據無關),然后在db中保存該加密串-用戶信息的對應關系,自動登錄時,用cookie中的加密串,到db中驗證,如果通過,自動登錄才算通過。

先在db中創建一張表:

復制代碼
 1 --Remember Me持久化保存記錄
 2 create table PERSISTENT_LOGINS
 3 (
 4   username  VARCHAR2(64) not null,
 5   series   VARCHAR2(64) not null,
 6   token     VARCHAR2(64) not null,
 7   last_used DATE not null
 8 );
 9 
10 alter table PERSISTENT_LOGINS
11   add constraint PK_PERSISTENT_LOGIN primary key (series);
復制代碼

然后將spring-security.xml中<remember-me/> 改為:

1 <remember-me data-source-ref="dataSource" 
2         token-validity-seconds="1209600" 
3         remember-me-parameter="remember-me" />

data-source-ref指定數據源,token-validity-seconds表示cookie的有效期(秒為單位),remember-me-parameter對應登錄頁上checkbox的名字。

這樣處理后,勾選Remember me登錄會在PERSISTENT_LOGINS表中,生成一條記錄:

logout時,該記錄以及客戶端的cookie都會同時清空。

最后,如果不想用默認的表名persistent_logins,可研究下:

org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl

org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices

這二個類的源碼 以及 相關文章:

http://forum.spring.io/forum/spring-projects/security/126343-spring-3-1-persistenttokenbasedremembermeservices-and-usernamepasswordauthentication

http://www.fengfly.com/document/springsecurity3/remember-me.html

http://docs.huihoo.com/spring/spring-security/3.0.x/remember-me.html#remember-me-persistent-token

示例源碼:SpringSecurity-Remember-Me-XML(0722).zip

參考文章:Spring Security Remember Me Example

http://www.cnblogs.com/yjmyzz/p/remember-me-sample-in-spring-security3.html

 

今天在前面一節的基礎之上,再增加一點新內容,默認情況下Spring Security不會對登錄錯誤的嘗試次數做限制,也就是說允許暴力嘗試,這顯然不夠安全,下面的內容將帶着大家一起學習如何限制登錄嘗試次數。

首先對之前創建的數據庫表做點小調整

一、表結構調整

T_USERS增加了如下3個字段:

D_ACCOUNTNONEXPIRED,NUMBER(1) -- 表示帳號是否未過期
D_ACCOUNTNONLOCKED,NUMBER(1), -- 表示帳號是否未鎖定
D_CREDENTIALSNONEXPIRED,NUMBER(1) --表示登錄憑據是否未過期

要實現登錄次數的限制,其實起作用的字段是D_ACCOUNTNONLOCKED,值為1時,表示正常,為0時表示被鎖定,另外二個字段的作用以后的學習內容會詳細解釋。

新增一張表T_USER_ATTEMPTS,用來輔助記錄每個用戶登錄錯誤時的嘗試次數

D_ID 是流水號

D_USERNAME 用戶名,外建引用T_USERS中的D_USERNAME

D_ATTEMPTS 登錄次數

D_LASTMODIFIED 最后登錄錯誤的日期

 

二、創建Model/DAO/DAOImpl

要對新加的T_USER_ATTEMPTS讀寫數據,得有一些操作DB的類,這里我們采用Spring的JDBCTemplate來處理,包結構參考下圖:

T_USER_ATTEMPTS表對應的Model如下

復制代碼
 1 package com.cnblogs.yjmyzz.model;
 2 
 3 import java.util.Date;
 4 
 5 public class UserAttempts {
 6 
 7     private int id;
 8 
 9     private String username;
10     private int attempts;
11     private Date lastModified;
12 
13     public int getId() {
14         return id;
15     }
16 
17     public void setId(int id) {
18         this.id = id;
19     }
20 
21     public String getUsername() {
22         return username;
23     }
24 
25     public void setUsername(String username) {
26         this.username = username;
27     }
28 
29     public int getAttempts() {
30         return attempts;
31     }
32 
33     public void setAttempts(int attempts) {
34         this.attempts = attempts;
35     }
36 
37     public Date getLastModified() {
38         return lastModified;
39     }
40 
41     public void setLastModified(Date lastModified) {
42         this.lastModified = lastModified;
43     }
44 
45 }
復制代碼

對應的DAO接口

  UserDetailsDao

以及DAO接口的實現

  UserDetailsDaoImpl

觀察代碼可以發現,對登錄嘗試次數的限制處理主要就在上面這個類中,登錄嘗試次數達到閾值3時,通過拋出異常LockedException來通知上層代碼。

 

三、創建CustomUserDetailsService、LimitLoginAuthenticationProvider

  CustomUserDetailsService

為什么需要這個類?因為下面這個類需要它:

  LimitLoginAuthenticationProvider

這個類繼承自org.springframework.security.authentication.dao.DaoAuthenticationProvider,而DaoAuthenticationProvider里需要一個UserDetailsService的實例,即我們剛才創建的CustomUserDetailService

LimitLoginAuthenticationProvider這個類如何使用呢?該配置文件出場了

 

四、spring-security.xml

復制代碼
 1 <beans:beans xmlns="http://www.springframework.org/schema/security"
 2     xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://www.springframework.org/schema/beans
 4     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 5     http://www.springframework.org/schema/security
 6     http://www.springframework.org/schema/security/spring-security-3.2.xsd">
 7 
 8     <http auto-config="true" use-expressions="true">
 9         <intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
10         <!-- access denied page -->
11         <access-denied-handler error-page="/403" />
12         <form-login login-page="/login" default-target-url="/welcome"
13             authentication-failure-url="/login?error" username-parameter="username"
14             password-parameter="password" />
15         <logout logout-success-url="/login?logout" />
16         <csrf />
17     </http>
18 
19     <beans:bean id="userDetailsDao"
20         class="com.cnblogs.yjmyzz.dao.impl.UserDetailsDaoImpl">
21         <beans:property name="dataSource" ref="dataSource" />
22     </beans:bean>
23 
24     <beans:bean id="customUserDetailsService"
25         class="com.cnblogs.yjmyzz.service.CustomUserDetailsService">
26         <beans:property name="usersByUsernameQuery"
27             value="SELECT d_username username,d_password password, d_enabled enabled,d_accountnonexpired accountnonexpired,d_accountnonlocked accountnonlocked,d_credentialsnonexpired credentialsnonexpired FROM t_users WHERE d_username=?" />
28         <beans:property name="authoritiesByUsernameQuery"
29             value="SELECT d_username username, d_role role FROM t_user_roles WHERE d_username=?" />
30         <beans:property name="dataSource" ref="dataSource" />
31     </beans:bean>
32 
33     <beans:bean id="authenticationProvider"
34         class="com.cnblogs.yjmyzz.provider.LimitLoginAuthenticationProvider">
35         <beans:property name="passwordEncoder" ref="encoder" />
36         <beans:property name="userDetailsService" ref="customUserDetailsService" />
37         <beans:property name="userDetailsDao" ref="userDetailsDao" />
38     </beans:bean>
39 
40     <beans:bean id="encoder"
41         class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
42         <beans:constructor-arg name="strength" value="9" />
43     </beans:bean>
44 
45 
46     <authentication-manager>
47         <authentication-provider ref="authenticationProvider" />
48     </authentication-manager>
49 
50 </beans:beans>
復制代碼

跟之前的變化有點大,47行是核心,為了實現47行的注入,需要33-38行,而為了完成authenticationProvider所需的一些property的注入,又需要其它bean的注入,所以看上去增加的內容就有點多了,但並不難理解。

 

五、運行效果

連續3次輸錯密碼后,將看到下面的提示

這時如果查下數據庫,會看到

錯誤嘗試次數,在db中已經達到閥值3

而且該用戶的“是否未鎖定”字段值為0,如果要手動解鎖,把該值恢復為1,並將T_USER_ATTEMPTS中的嘗試次數,改到3以下即可。

 

源代碼下載:SpringSecurity-Limit-Login-Attempts-XML.zip
參考文章: Spring Security : limit login attempts example

http://www.cnblogs.com/yjmyzz/p/limit-login-attempts-in-spring-security3.html

在前一節使用數據庫進行用戶認證(form login using database)里,我們學習了如何把“登錄帳號、密碼”存儲在db中,但是密碼都是明文存儲的,顯然不太講究。這一節將學習如何使用spring security3新加入的bcrypt算法,將登錄加密存儲到db中,並正常通過驗證。

一、Bcrypt算法

復制代碼
 1 int t = 0;
 2 String password = "123456";
 3 System.out.println(password + " -> ");
 4 for (t = 1; t <= 10; t++) {
 5     BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
 6     String hashedPassword = passwordEncoder.encode(password);
 7     System.out.println(hashedPassword);
 8 }
 9 
10 password = "MIKE123";
11 System.out.println(password + " -> ");
12 for (t = 1; t <= 10; t++) {
13     BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
14     String hashedPassword = passwordEncoder.encode(password);
15     System.out.println(hashedPassword);
16 }
復制代碼

輸出如下:

 123456 -> 
 2a2a10$.Cjkvbgr2JzGkag9IdbT.Oc/sbY7wVqLgAHws7HCxqcI7eczKtCLq
 2a2a10$OCOuRV0Wy7ncCND4LcKfMunVEWOzMOyyU95u5TkTRmJqYbsJNecEK
 2a2a10$TXttsDZUaeEb2zX6wiwN0eqREKFoCDyh81Kfa6BgAcZ2hyqPNC0Ra
 2a2a10$FfLx/gxq.FyeOBb0nbaVeusLhQjASSdY7w45i1ACl/rcYQMmhaXV2
 2a2a10$JdPXAxmuz.WTP5gxYiYseeKRSM/HTFzJJdACcDQ4MdhaaLmC0SjI.
 2a2a10$yVEWf2MrwjCyi51rUKqQle/MZb7vwcOf6Gwp.hDT2ZUchlyAtJ4pO
 2a2a10$FfJg2ATit7btKfJovL6zmug//8rzToQn7FO.fxOzo1KtNNfhWKuca
 2a2a10$pOLMkd13n7i3DtVijLEqze1zeURpjtVz5rAx1qOAPqCQvjGG/d6D.
 2a2a10$fQ32i8JsjjmqVRpiEsgT3ekTKtrfXn.JNl69beWEx0.YgdX.SEx5e
 2a2a10$78brJFSdftip0XXYx4rS6ewdu4SiSsMIBY9oNcLhAZwg3GysRGk2m
 MIKE123 -> 
 2a2a10$U6KVh1NGxAIGYiM4YVgn6OAQt6ayAoLkh2lODv16rSpkS1iqfbR2C
 2a2a10$t0FlEOBLEB8VwWJVoZRrweIRV0XyoBgm29c0SMqfqRK3ZBuvhgYbS
 2a2a10$QpW6nHnWNhbTTjLq/NbzBu2Unp8ijwyPeUx2N2eMFWReFezosZ5fi
 2a2a10$LtPzoQU0IluAgvP3/WhWquUv2AcDRh2ENhAeWDquiN/spitZYe/7q
 2a2a10$Qcx7vUudzF7qzTjz.QpLKOby0tXQ4j.uqkInS1n4/6oD2r2eL0rZW
 2a2a10$yZw7cdq1y9sjX8nZhYynseWjQ4jeVv76fPmBl.sg2xPvb8cyXD8Sq
 2a2a10$kTmT6BQQE5LyRZ00Qas77.F5kxK0GxsW402ExosQswxmG.eBdgIZW
 2a2a10$SRfHDNM.m3qX5y1O7V/cp.hQqgaXnKzfxBGRhLkAF39bufejuOieu
 2a2a10$Sw5w2kTImJ5Y8UNlE/5/9OLaUgYxhCXU3P3gFBdEbs9PL8pCl60Q2
 2a2a10$0mN8kNAl9GNr0c4K1Nr0b.MIcBW0QcPHB/f20hgeBuRfwvgZXT6hG
從以上輸出結果發現bcrypt算法與md5/sha算法有一個很大的區別,每次生成的hash值都是不同的,這樣暴力猜解起來或許要更困難一些。同時大家可能也發現了,加密后的字符長度比較長,有60位,所以用戶表中密碼字段的長度,如果打算采用bcrypt加密存儲,字段長度不得低於60.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM