SpringBoot中使用Spring Security

Spring Security 基本介紹

這里就不對Spring Security進行過多的介紹了,具體的可以參考官方文檔
我就只說下SpringSecurity核心功能:

  • 認證(你是誰)
  • 授權(你能干什么)
  • 攻擊防護(防止偽造身份)

基本環境搭建

這里我們以SpringBoot作為項目的基本框架,我這里使用的是maven的方式來進行的包管理,所以這里先給出集成Spring Security的方式

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

       <!-- thymeleaf模板 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

然后建立一個Web層請求接口

@Controller
public class IndexController {

    /**
     * 跳轉首頁
     */
    @GetMapping("")
    public void index1(HttpServletResponse response){
        //內部重定向
        try {
            response.sendRedirect("/index");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  
    /**
     * 首頁
     */
    @RequestMapping("/index")
    @ResponseBody
    public String index() {
        return "index";
    }
}

接下來可以直接進行項目的運行,并進行接口的調用看看效果了。

通過網頁的調用

我們首先通過瀏覽器進行接口的調用,直接訪問http://localhost:8080/index,如果接口能正常訪問,那么應該顯示“index”。
但是我們是沒法正常訪問的,出現了下圖的身份驗證輸入框

image.png

這是因為在SpringBoot中,默認的Spring Security就是生效了的,此時的接口都是被保護的,我們需要通過驗證才能正常的訪問。 Spring Security提供了一個默認的用戶,用戶名是user,而密碼則是啟動項目的時候自動生成的。
我們查看項目啟動的日志,會發現如下的一段Log

Using default security password: 62ccf9ca-9fbe-4993-8566-8468cc33c28c

當然你看到的password肯定和我是不一樣的,我們直接用user和啟動日志中的密碼進行登錄。

登錄成功后,就跳轉到了接口正常調用的頁面了。
如果不想一開始就使能Spring Security,可以在配置文件中做如下的配置:

# security 使能
security.basic.enabled = false

剛才看到的登錄框是SpringSecurity是框架自己提供的,被稱為httpBasicLogin。顯示它不是我們產品上想要的,我們前端一般是通過表單提交的方式進行用戶登錄驗證的,所以我們就需要自定義自己的認證邏輯了。

改造1 使用頁面表單登錄

  1. 前端寫一個登陸頁面(使用 thymeleaf 模板引擎),login_page.html文件:
<!DOCTYPE html>
<html id="ng-app" ng-app="app"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>home</title>
</head>
<body>
<form  class="form-signin" action="/form" method="post">
    <h2 class="form-signin-heading">用戶登錄</h2>
    <table>
        <tr>
            <td>用戶名:</td>
            <td><input type="text" name="username"  class="form-control"  placeholder="請輸入用戶名"/></td>
        </tr>
        <tr>
            <td>密碼:</td>
            <td><input type="password" name="password"  class="form-control" placeholder="請輸入密碼" /></td>
        </tr>
        <tr>

            <td colspan="2">
                <button type="submit"  class="btn btn-lg btn-primary btn-block" >登錄</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

寫一個controller方法指向該登陸頁面,不能使用@RestController@ResponseBody,否則就返回字符串了。

 @RequestMapping("/loginPage")
    public String login() {
        return "login_page";
    }

還需要配置上:

spring:
  # 定位模板的目錄,給返回的頁面添加后綴名
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    servlet:
      content-type: text/html
    mode: HTML5
  1. 添加一個類 SecurityConfig 繼承 WebSecurityConfigurerAdapter
    重寫configure方法,并加上@Configuration 和@EnableWebSecurity 2個注解。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

     @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 關閉csrf防護
                .csrf().disable()
                .headers().frameOptions().disable()
                .and();
        http
                //登錄處理
                .formLogin() //表單方式,或httpBasic
                .loginPage("/loginPage")
                .loginProcessingUrl("/form")
                .defaultSuccessUrl("/index") //成功登陸后跳轉頁面
                .failureUrl("/loginError")
                .permitAll()
                .and();
        http
                .authorizeRequests() // 授權配置
                //無需權限訪問
                .antMatchers( "/css/**", "/error404").permitAll()
                .antMatchers("/user/**").hasRole("USER")
                //其他接口需要登錄后才能訪問
                .anyRequest().authenticated()
                .and();
    }
  
}

與/ css / **和/ error404匹配的請求是完全可訪問的
與/ user / **匹配的請求要求用戶進行身份驗證,并且必須與USER角色相關聯
使用自定義登錄頁面和失敗URL啟用基于表單的身份驗證
loginPage("/login")表示登錄時跳轉的頁面,因為登錄頁面我們不需要登錄認證,所以我們需要添加 permitAll() 方法。

login-page 自定義登錄頁url,默認為/login
login-processing-url 登錄請求攔截的url,也就是form表單提交時指定的action
failureUrl=表示登錄出錯的頁面,我們可以簡單寫個提示:如 用戶名或密碼錯誤。

.csrf().disable() 說明:Spring Security4默認是開啟CSRF的,所以需要請求中包含CSRF的token信息,這里不添加這段代碼的話會出現異常,加上的話可以關閉csrf(關閉后有安全漏洞)。

測試:
1、輸入網址:http://127.0.0.1:8081/index,自動跳轉到:http://127.0.0.1:8081/loginPage,返回登陸頁面
2、輸入賬號密碼:錯誤的話返回http://127.0.0.1:8081/loginError,登陸失敗頁面
正確的話:返回http://127.0.0.1:8081/index,登陸成功頁面

改造2、自定義用戶名和密碼

1、用戶直接寫入內存中

很顯然,這樣改造之后,雖然登錄頁面是好看了,但還遠遠不能滿足我們的應用需求,所以第二步,我們改造自定義的用戶名和密碼。
自定義用戶名和密碼有2種方式,一種是在代碼中寫死,這也是官方的demo,另一種是使用數據庫
首先是第一種:如

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

@Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .passwordEncoder(passwordEncoder())
                .withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN")
                .and()
                .withUser("test").password(passwordEncoder().encode("test123")).roles("USER");

    }

新的版本需要對密碼進行加密,不然會報錯,所以這里要使用passwordEncoder,程序運行起來,這時用我們自己的用戶名和密碼 輸入 admin 和123456 就可以了。
你也可以多幾個用戶,就多幾個withUser即可。

.and().withUser("test").password("test123").roles("ADMIN");
這樣我們就有了一個用戶名為test,密碼為test123的用戶了。

2、使用數據庫的用戶

第一種的只是讓我們體驗了一下Spring Security而已,我們接下來就要提供自定義的用戶認證機制及處理過程。
在講這個之前,我們需要知道spring security的原理,spring security的原理就是使用很多的攔截器對URL進行攔截,以此來管理登錄驗證和用戶權限驗證

用戶登陸,會被AuthenticationProcessingFilter攔截,調用AuthenticationManager的實現,而且AuthenticationManager會調用ProviderManager來獲取用戶驗證信息(不同的Provider調用的服務不同,因為這些信息可以是在數據庫上,可以是在LDAP服務器上,可以是xml配置文件上等),如果驗證通過后會將用戶的權限信息封裝一個User放到spring的全局緩存SecurityContextHolder中,以備后面訪問資源時使用。

所以我們要自定義用戶的校驗機制的話,我們只要實現自己的AuthenticationProvider就可以了。在用AuthenticationProvider 這個之前,我們需要提供一個獲取用戶信息的服務,實現 UserDetailsService 接口

用戶名密碼->(Authentication(未認證) -> AuthenticationManager ->AuthenticationProvider->UserDetailService->UserDetails->Authentication(已認證)

了解了這個原理之后,我們就開始寫代碼

UserDetails接口

UserDetailsService接口

實現 UserDetailsService接口 來返回User的對象實例

@Component
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private SysUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //這里可以可以通過username(登錄時輸入的用戶名)然后到數據庫中找到對應的用戶信息,并構建成我們自己的UserInfo來返回。
        SysUser sysUser = userService.findUserByName(username);
        //由于權限參數不能為空,所以這里先使用AuthorityUtils.commaSeparatedStringToAuthorityList方法模擬一個admin的權限,該方法可以將逗號分隔的字符串轉換為權限集合。
//數據庫中的密碼是加密后的
        return new User(username, sysUser.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

一般而言登錄的數據在protected void configure(AuthenticationManagerBuilder auth)中,所有需要修改SecurityConfig類里面的configure(AuthenticationManagerBuilder auth)方法

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                //用戶認證處理
                .userDetailsService(userDetailsService)
                //密碼處理
                .passwordEncoder(passwordEncoder());
    }

重新運行項目,這時候就可以使用數據庫的賬號和密碼登陸了。

一般而言到這里就可以實現登陸驗證了。但是遇到一個問題,這樣的話用戶名和密碼都是定死的,我們拿不到form-data數據,如果因為前端的問題,這種密碼登錄方式以外,我們還要稍微修改提交給我們的form-data中的密碼數據,做一下處理,自定義一個登錄呢。這個時候就需要用到AuthenticationProvider了。

AuthenticationProvider接口

這是一個接口,提供了兩種方法

public interface AuthenticationProvider {
  
  Authentication authenticate(Authentication authentication)
          throws AuthenticationException;

  boolean supports(Class<?> authentication);
}

通過第一個方法我們可以拿到form-data的數據,并且返回一個UserDetails如果登錄成功的話,或者返回null如果登錄失敗。

@Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = (String) authentication.getPrincipal(); //拿到username
        String password = (String) authentication.getCredentials(); //拿到password
       
        UserDetails userDetails = studentService.loadUserByUsername(userName);
        if (/*自定義的驗證通過*/) {
            return new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
        }
        /*驗證不通過*/
        return null;

第二個方法是告訴spring sec我們這個驗證支持哪種驗證。

auth.userDetailsService(studentService).passwordEncoder(encoder);

這種驗證屬于Dao驗證。

還有UsernamePasswordAuthentication驗證。
我們的是UsernamePassword的驗證方式,所以在第二個方法中一般會這么寫

   @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

新建類 MyAuthenticationProvider 繼承AuthenticationProvider
完整的代碼如下:

@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    /**
     * 注入我們自己定義的用戶信息獲取對象
     */
    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // TODO Auto-generated method stub
        String userName = authentication.getName();// 這個獲取表單輸入中返回的用戶名;
        String password = (String) authentication.getCredentials();// 這個是表單中輸入的密碼;
        // 這里構建來判斷用戶是否存在和密碼是否正確
        UserDetails userInfo = userDetailsService.loadUserByUsername(userName); // 這里調用我們的自己寫的獲取用戶的方法;
        if (userInfo == null) {
            throw new BadCredentialsException("用戶名不存在");
        }
        boolean flag = passwordEncoder.matches(password,userInfo.getPassword());
        if (!flag) {
            throw new BadCredentialsException("密碼不正確");
        }
        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        // 構建返回的用戶登錄成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
        //return new UsernamePasswordAuthenticationToken(userInfo, null,authorities);
    }
    @Override
    public boolean supports(Class<?> authentication) {
        // 這里直接改成retrun true;表示是支持這個執行
        return true;
    }
}

到此為止,我們的用戶信息的獲取,校驗部分已經完成了。接下來要讓它起作用,則我們需要在配置文件中修改,讓他起作用。回到我的SecurityConfig代碼文件,修改如下:

1、注入我們自己的AuthenticationProvider
2、修改配置的方法:

    @Autowired
    private AuthenticationProvider provider;  //注入我們自己的AuthenticationProvider

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(provider);
    }

現在重新運行程序,則需要輸入用戶名為 admin 密碼是123456之后,才能正常登錄了。
為了方便測試,我們調整添加另一個控制器 /whoim 的代碼 ,讓他返回當前登錄的用戶信息,前面說了,他是存在SecurityContextHolder 的全局變量中,所以我們可以這樣獲取

      @RequestMapping("/whoim")
      @ResponseBody
      public Object whoIm()
      {
            return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      }

我們運行,直接反問 /whoim ,則直接跳轉到登錄頁面,我們驗證過之后,再訪問此url,結果如下:


image.png

改造3、自定義登錄成功和失敗的處理邏輯

在現在的大多數應用中,一般都是前后端分離的,所以我們登錄成功或失敗都需要用json格式返回,或者登錄成功之后,跳轉到某個具體的頁面。
接下來我們來實現這種改造。

為了實現這個功能,只要實現AuthenticationSuccessHandler接口的onAuthenticationSuccess方法即可。
處理登錄成功的:

@Component
public class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {
    private RequestCache requestCache = new HttpSessionRequestCache();
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        //response.setContentType("application/json;charset=utf-8");
        //response.getWriter().write(objectMapper.writeValueAsString(authentication));
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (savedRequest == null){
            redirectStrategy.sendRedirect(request, response, "/index");
        }else {
            System.out.println(savedRequest.getRedirectUrl());
            redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());
        }

    }
}

和自定義登錄成功處理邏輯類似,自定義登錄失敗處理邏輯需要實現AuthenticationFailureHandleronAuthenticationFailure方法:

@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Autowired
    private ObjectMapper mapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));
    }
}

代碼完成之后,修改配置config類代碼。
添加2個注解,自動注入

@Autowired
      private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
      @Autowired
      private AuthenticationFailureHandler myAuthenticationFailHander;
      
      @Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
                  .and()
                  .authorizeRequests().anyRequest().authenticated()                  
                  .and()
                  .csrf().disable();            
      }

進行測試,成功返回json格式的(登錄成功和失敗的)

改造4、添加權限控制

之前的代碼我們用戶的權限沒有加以利用,現在我們添加權限的用法。
之前的登錄驗證通俗的說,就是來判斷你是誰(認證),
而權限控制就是用來確定:你能做什么或者不能做什么(權限)

在講這個之前,我們簡單說下,對于一些資源不需要權限認證的,那么就可以在Config中添加 過濾條件,如:

@Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
                  .and()
                  .authorizeRequests()
                        .antMatchers("/index").permitAll()  //這就表示 /index這個頁面不需要權限認證,所有人都可以訪問
                  .anyRequest().authenticated()  //其他的需要做權限認證            
                  .and()
                  .csrf().disable();            
      }

那么我們直接訪問 /index 就不會跳轉到登錄頁面,這樣我們就可以把一些不需要驗證的資源以這種方式過濾,比如圖片,腳本,樣式文件之類的。

我們先來看第一種權限控制:在編碼中寫死的。
其實權限控制也是通過這種方式來實現:

   http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
                  .and()
                  .authorizeRequests()
                        .antMatchers("/index").permitAll()                    
                  .antMatchers("/whoim").hasRole("ADMIN") //這就表示/whoim的這個資源需要有ROLE_ADMIN的這個角色才能訪問。不然就會提示拒絕訪問
                  .anyRequest().authenticated() //必須經過認證以后才能訪問          
                  .and()
                  .csrf().disable();   

這個用戶的角色哪里來,就是我們自己的UserDetailsService中返回的用戶信息中的角色權限信息,
這里需要注意一下就是 .hasRole("ADMIN"),那么給用戶的角色時就要用:ROLE_ADMIN

.antMatchers 這里也可以限定HttpMethod的不同要求不同的權限(適用于Restful風格的API).
如:Post需要 管理員權限,get 需要user權限,我們可以這么個改造,同時也可以通過通配符來是實現 如:/user/1 這種帶參數的URL

.antMatchers("/whoim").hasRole("ADMIN")
.antMatchers(HttpMethod.POST,"/user/").hasRole("ADMIN")
.antMatchers(HttpMethod.GET,"/user/
").hasRole("USER")

Spring Security 的校驗的原理:左手配置信息,右手登錄后的用戶信息,中間投票器。
從我們的配置信息中獲取相關的URL和需要的權限信息,然后獲得登錄后的用戶信息,
然后經過:AccessDecisionManager 來驗證,這里面有多個投票器:AccessDecisionVoter,(默認有幾種實現:比如:1票否決(只要有一個不同意,就沒有權限),全票通過,才算通過;只要有1個通過,就全部通過。類似這種的。
WebExpressionVoter 是Spring Security默認提供的的web開發的投票器。(表達式的投票器)

Spring Security 默認的是 AffirmativeBased 只要有一個通過,就通過。
有興趣的可以 從FilterSecurityInterceptor這個過濾器入口,來查看這個流程。
內嵌的表達式有:permitAll denyAll 等等。
每一個權限表達式都對應一個方法。
如果需要同時滿足多個要求的,不能連寫如 ,我們有個URL需要管理員權限也同時要限定IP的話,不能:.hasRole("ADMIN").hasIPAddress("192.168.1.1");
而是需要用access方法 .access("hasRole('ADMIN') and hasIpAddress('192.168.1.1')");這種。

那我們可以自己寫權限表達式嗎? 可以,稍后。。。這些都是硬編碼的實現,都是在代碼中寫入的,這樣的靈活性不夠。所以我們接下來繼續改造

改造4、添加基于RBAC(role-Based-access control)權限控制

這個大家可以去百度一下,一般都是由 3個部分組成,一個是用戶,一個是角色 ,一個是資源(菜單,按鈕),然后就是 用戶和角色的關聯表,角色和資源的關聯表

核心就是判斷當前的用戶所擁有的URL是否和當前訪問的URL是否匹配。

首先我們自己提供一個判斷的接口和實現,代碼如下:

/**
 * 返回權限驗證的接口
 */
public interface RbacService {
      boolean hasPermission(HttpServletRequest request,Authentication authentication);
}

@Component("rbacService")
public class RbacServiceImpl implements RbacService {
      private AntPathMatcher antPathMatcher = new AntPathMatcher();
      @Override
      public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
            Object principal = authentication.getPrincipal();
            boolean hasPermission = false;
            if (principal instanceof UserDetails) { //首先判斷先當前用戶是否是我們UserDetails對象。
                  String userName = ((UserDetails) principal).getUsername();
                  Set<String> urls = new HashSet<>(); // 數據庫讀取 //讀取用戶所擁有權限的所有URL
                  
                  urls.add("/whoim");
                  // 注意這里不能用equal來判斷,因為有些URL是有參數的,所以要用AntPathMatcher來比較
                  for (String url : urls) {
                        if (antPathMatcher.match(url, request.getRequestURI())) {
                              hasPermission = true;
                              break;
                        }
                  }
            }
            return hasPermission;
      }
}

然后在Security的配置項中添加自定義的權限表達式就可以了。

@Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
                  .and()
                  .authorizeRequests()
//                      .antMatchers("/index").permitAll()                    
//                .antMatchers("/whoim").hasRole("ADMIN")
//                .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
//                .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
                  .anyRequest().access("@rbacService.hasPermission(request,authentication)")    //必須經過認證以后才能訪問            
                  .and()
                  .csrf().disable();            
      }

其中 @rbacService 就是我們自己聲明的bean,在RbacServiceImpl實現類的頭部注解中。

改造5、記住我的功能Remeber me

本質是通過token來讀取用戶信息,所以服務端需要存儲下token信息
根據官方的文檔,token可以通過數據庫存儲 數據庫腳本

CREATE TABLE persistent_logins (
    username VARCHAR(64) NOT NULL,
    series VARCHAR(64) NOT NULL,
    token VARCHAR(64) NOT NULL,
    last_used TIMESTAMP NOT NULL,
    PRIMARY KEY (series)
);

然后,配置好token 的存儲 及數據源

 @Autowired
      private DataSource dataSource;   //是在application.properites

      /**
       * 記住我功能的token存取器配置
       * @return
       */
      @Bean
      public PersistentTokenRepository persistentTokenRepository() {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
      }

修改Security配置

  @Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
                  .and()
                  .rememberMe()
                        .rememberMeParameter("remember-me").userDetailsService(userDetailsService)
                        .tokenRepository(persistentTokenRepository())
                        .tokenValiditySeconds(60)
                  .and()
                  .authorizeRequests()
//                      .antMatchers("/index").permitAll()                    
//                .antMatchers("/whoim").hasRole("ADMIN")
//                .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
//                .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
                  .anyRequest().access("@rbacService.hasPermission(request,authentication)")    //必須經過認證以后才能訪問            
                  .and()
                  .csrf().disable();      

在登陸頁面login.html上還要加上記住密碼的勾選框

 <tr>
            <td colspan="2"><input type="checkbox" name="remember-me" value="true"/>記住我</td>
        </tr>

登錄之后 數據庫就會有一條數據


image.png

然后,服務重新啟動下,我們在看下直接訪問 /whoim 的話,就可以直接訪問了,不需要再登錄了。

到此為止我們的Spring Securtiy 的基本用法已經改造完成了。

源碼鏈接:https://github.com/visionsws/vicente-demo


參考文章
Spring boot 中 Spring Security 使用改造5部曲
SpringBoot + Spring Security 基本使用及個性化登錄配置
Hello Spring Security with Boot

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容