<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          超全的springboot+springsecurity實現前后端分離簡單實現!

          共 9047字,需瀏覽 19分鐘

           ·

          2020-12-13 00:50

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質文章,第一時間送達

          66套java從入門到精通實戰(zhàn)課程分享

          1、前言部分

          1.1、嘮嗑部分(如何學習?)

          ? 看springsecurtiy原理圖的時候以為灑灑水,媽的,結果自己動手做的時候一竅不通,所以一定不要眼高手低,實踐出真知!通過各種方式學習springsecurity,在B站、騰訊課堂、網易課堂、慕課網沒有springsecurity的前后端分離的教學視頻,那我就去csdn去尋找springsecurity博客,發(fā)現幾個問題:

          • 要么就是前后端不分離,要么就是通過內存方式讀取數據,而不是通過數據庫的方式讀取數據,要么就是大佬們給的代碼不全、把代碼講的太繞,關鍵部分沒有注釋。

          • 講的例子不那么通俗易懂,不利于新手的學習。

          • 代碼本身有bug,或者就沒有我想要實現的效果。

          ? 實在不行我又跑去github上找開源項目學習,github由于是外國網站,國內訪問速度有點慢??!那就用國內的gitee吧,gitee上的開源項目都是結合實戰(zhàn)項目的,代碼邏輯也比較復雜,我對項目的業(yè)務邏輯沒什么了解,感覺不適合我。我這一次選擇比較反人性的方式去學習,就是手撕源碼和看官方文檔。老實講,剛開始看源碼官方文檔特別難受,并且看不進去,那些springsecurity的類還有接口名字又臭又長,這時我就下載源碼,源碼的注釋多的就像一本書,非常詳細且權威。

          ? 當然別指望看幾遍就能看懂,我看這些注釋、源碼、博客看了10幾遍甚至20幾遍才看懂,每次去看都有不同的收獲?。?!

          ?此文章截圖水平不高、理解為主、欣賞為輔?。热萦悬c多,每一步都有詳細解析,請耐心看完,看不懂可以多看幾遍。。

          1.2、技術支持

          ? jdk 1.8、springboot 2.3.4、mybatis-plus 3.4.1、mysql 5.5、springsecurity 5.3.4、springmvc、lombok簡化entity代碼,不用你去寫get、set方法,全部自動生成、gson 2.8.2 將json對象轉化成json字符串

          1.3、預期實現效果圖

          未登錄時訪問指定資源, 返回未登錄的json字符串 ,?index是我在controller層寫的一個簡單接口,返回index字符串

          輸入賬號錯誤,返回用戶名錯誤的json字符串 , 需說明一點,/login是springsecurity封裝好的接口,無須你在controller寫login接口,/logout也同理。

          輸入密碼錯誤,返回密碼錯誤的json字符串

          登錄成功, 返回登錄成功的json字符串并返回cookie

          登錄成功并且擁有權限訪問指定資源, 返回資源相關數據的json字符串

          ?

          ?登錄成功但無權限訪問指定資源時,返回權限不足的json字符串

          異地登錄,返回異地登錄,強制下線的json字符串 ,?測試的基礎是要在兩臺不同的機器上登錄,然后訪問/index。

          ?注銷成功,返回注銷成功的json字符串并刪除cookie

          ?2、核心部分

          2.1、springsecurity原理解釋:

          ? springsecurity最重要的兩個部分:?authentication(認證) 和 authorization(授權)

          ??認證: 就是判定你是什么身份,管理員還是普通人

          ??授權: 什么樣的身份擁有什么樣的權利。

          ?

          ?

          簡單理解:?自定義配置登錄成功、登陸失敗、注銷成功目標結果類,并將其注入到springsecurity的配置文件中。如何認證、授權交給AuthenticationManager去作

          復雜理解:?

          (1)用戶發(fā)起表單登錄請求后,首先進入?UsernamePasswordAuthenticationFilter, 在?UsernamePasswordAuthenticationFilter中根據用戶輸入的用戶名、密碼構建了?UsernamePasswordAuthenticationToken,并將其交給?AuthenticationManager?來進行認證處理。

          AuthenticationManager 本身不包含認證邏輯,其核心是用來管理所有的?AuthenticationProvider,通過交由合適的 AuthenticationProvider 來實現認證。

          (2)下面跳轉到了?SelfAuthenticationProvider,該類是?AuthenticationProvider?的實現類:你可以在該類的?Authentication authenticate(Authentication authentication)?自定義認證邏輯, 然后在該類中通過調用UserDetails loadUserByUsername(account)?去獲取數據庫用戶信息并驗證,然后創(chuàng)建?UsernamePasswordAuthenticationToken?并將權限、用戶個人信息注入到其中 ,并通過setAuthenticated(true)?設置為需要驗證。

          (3)?至此認證信息就被傳遞回?UsernamePasswordAuthenticationFilter?中,在?UsernamePasswordAuthenticationFilter?的父類?AbstractAuthenticationProcessingFilter?的?doFilter()?中,會根據認證的成功或者失敗調用相應的?handler:所謂的handler就是我們注入到springsecurity配置文件的handler。

          ?

          2.2、踩坑集錦

          • 訪問/login時必須要用post方法!, 訪問的參數名必須為username和password ??

          • 訪問/logout時即可用post也可用get方法!

          • //springsecurity配置文件中的hasRole("")不能以ROLE開頭,比如ROLE_USER就是錯的,springsecurity會默認幫我們加上,但數據庫的權限字段必須是ROLE_開頭,否則讀取不到

          .antMatchers("/index").hasRole("USER")
          ? ? ? ?.antMatchers("/hello").hasRole("ADMIN")

          ?

          2.3、代碼部分

          pom依賴文件

          ?
          ????????
          ????????
          ????????????com.google.code.gson
          ????????????gson
          ????????????2.8.2
          ????????

          ????????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-web
          ????????

          ????????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-security
          ????????

          ????????
          ????????
          ????????????mysql
          ????????????mysql-connector-java
          ????????????runtime
          ????????

          ????????
          ????????
          ????????????org.projectlombok
          ????????????lombok
          ????????????true
          ????????

          ????????
          ????????
          ????????????com.baomidou
          ????????????mybatis-plus-boot-starter
          ????????????3.4.1
          ????????

          ??????
          ????????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-test
          ????????????test
          ????????????
          ????????????????
          ????????????????????org.junit.vintage
          ????????????????????junit-vintage-engine
          ????????????????

          ????????????

          ????????

          ????????
          ????????
          ????????????org.springframework.security
          ????????????spring-security-config
          ????????????5.3.4.RELEASE
          ????????

          ????????
          ????????????org.springframework.security
          ????????????spring-security-web
          ????????????5.3.4.RELEASE
          ????????

          ????


          ?Msg.java?自定義返回結果集,這個看個人的,怎么開心怎么來!

          @Data
          @NoArgsConstructor
          @AllArgsConstructor
          public?class?Msg?{
          ????int?code;???//錯誤碼
          ????String?Message;?//消息提示
          ????Map?data=new?HashMap();???//數據
          ?
          ????//無權訪問
          ????public?static?Msg?denyAccess(String?message){
          ????????Msg?result=new?Msg();
          ????????result.setCode(300);
          ????????result.setMessage(message);
          ????????return?result;
          ????}
          ?
          ????//操作成功
          ????public?static?Msg?success(String?message){
          ????????Msg?result=new?Msg();
          ????????result.setCode(200);
          ????????result.setMessage(message);
          ????????return?result;
          ????}
          ?
          ????//客戶端操作失敗
          ????public?static?Msg?fail(String?message){
          ????????Msg?result=new?Msg();
          ????????result.setCode(400);
          ????????result.setMessage(message);
          ????????return?result;
          ????}
          ?
          ????public?Msg?add(String?key,Object?value){
          ????????this.data.put(key,value);
          ????????return?this;
          ????}
          }


          ?User.java ,此類是entity實體類

          @Data
          public?class?User?implements?Serializable?{
          ?
          ????private?Integer?id;?????
          ?
          ????private?String?account;
          ?
          ????private?String?password;
          ?
          ????private?String?role;
          ????
          }


          UserMapper.java ,此接口繼承?BaseMapper,BaseMapper類?封裝了大量的sql,極大程度簡化了程序員sql語句的書寫.

          @Repository
          public?interface?UserMapper?extends?BaseMapper?{
          ?
          }


          正常情況下要寫UserService.java接口,但是此文章只是用于演示效果,就沒書寫了

          public?interface?UserService{
          ?
          }


          UserServiceImpl.java,使其實現?UserDetailsService接口,??從而去獲取數據庫用戶信息,詳細解析請看注釋部分。

          @Service
          public?class?UserServiceImpl?extends?ServiceImpl?implements?UserService,UserDetailsService?{
          ?
          ????@Autowired
          ????UserMapper?userMapper;
          ?
          ????//加載用戶
          ????@Override
          ????public?UserDetails?loadUserByUsername(String?s)?throws?UsernameNotFoundException?{
          ????????//mybatis-plus幫我們寫好了sql語句,相當于?select?*?from?user?where?account?='${account}'
          ????????QueryWrapper?wrapper=new?QueryWrapper<>();
          ????????wrapper.eq("account",s);
          ????????User?user=userMapper.selectOne(wrapper);????//user即為查詢結果
          ????????if(user==null){
          ????????????throw?new?UsernameNotFoundException("用戶名錯誤!!");
          ????????}
          ?
          ????????//獲取用戶權限,并把其添加到GrantedAuthority中
          ????????List?grantedAuthorities=new?ArrayList<>();
          ????????GrantedAuthority?grantedAuthority=new?SimpleGrantedAuthority(user.getRole());
          ????????grantedAuthorities.add(grantedAuthority);
          ?
          ????????//方法的返回值要求返回UserDetails這個數據類型,??UserDetails是接口,找它的實現類就好了
          ????????//new?org.springframework.security.core.userdetails.User(String?username,String?password,Collection?authorities)?就是它的實現類
          ????????return?new?org.springframework.security.core.userdetails.User(s,user.getPassword(),grantedAuthorities);
          ????}
          }


          UserController.java 就是普通的controller

          @RestController
          public?class?UserController?{
          ????@GetMapping("index")
          ????public?String?index(){
          ????????return?"index";
          ????}
          ?
          ????@GetMapping("hello")
          ????public?String?hello(){
          ????????return?"hello";
          ????}
          }


          AuthenticationEnryPoint .java? ?自定義未登錄的處理邏輯??

          @Component
          public?class?AuthenticationEnryPoint?implements?AuthenticationEntryPoint?{
          ?
          ????@Autowired
          ????Gson?gson;
          ?
          ????//未登錄時返回給前端數據
          ????@Override
          ????public?void?commence(HttpServletRequest?request,?HttpServletResponse?response,?AuthenticationException?e)?throws?IOException,?ServletException?{
          ????????Msg?result=Msg.fail("需要登錄!!");
          ????????response.setContentType("application/json;charset=utf-8");
          ????????response.getWriter().write(gson.toJson(result));
          ????}
          }


          AuthenticationFailure.java 自定義登錄失敗時的處理邏輯

          //登錄失敗返回給前端消息
          @Component
          public?class?AuthenticationFailure?implements?AuthenticationFailureHandler{
          ????@Autowired
          ????Gson?gson;
          ?
          ????@Override
          ????public?void?onAuthenticationFailure(HttpServletRequest?request,?HttpServletResponse?response,?AuthenticationException?e)?throws?IOException,?ServletException?{
          ????????Msg?msg=null;
          ????????if(e?instanceof?UsernameNotFoundException){
          ????????????msg=Msg.fail(e.getMessage());
          ????????}else?if(e?instanceof?BadCredentialsException){
          ????????????msg=Msg.fail("密碼錯誤!!");
          ????????}else?{
          ????????????msg=Msg.fail(e.getMessage());
          ????????}
          ????????//處理編碼方式,防止中文亂碼的情況
          ????????response.setContentType("text/json;charset=utf-8");
          ????????//返回給前臺
          ????????response.getWriter().write(gson.toJson(msg));
          ????}
          }


          AuthenticationSuccess.java 自定義登錄成功時的處理邏輯

          @Component
          public?class?AuthenticationSuccess?implements?AuthenticationSuccessHandler{
          ????@Autowired
          ????Gson?gson;
          ?
          ????@Override
          ????public?void?onAuthenticationSuccess(HttpServletRequest?request,?HttpServletResponse?response,?Authentication?authentication)?throws?IOException,?ServletException?{
          ????????//登錄成功時返回給前端的數據
          ????????Msg?result=Msg.success("登錄成功?。。。?!");
          ????????response.setContentType("application/json;charset=utf-8");
          ????????response.getWriter().write(gson.toJson(result));
          ????}
          }


          AuthenticationLogout.java 自定義注銷時的處理邏輯

          @Component
          public?class?AuthenticationLogout?implements?LogoutSuccessHandler{
          ????@Autowired
          ????Gson?gson;
          ?
          ????@Override
          ????public?void?onLogoutSuccess(HttpServletRequest?request,?HttpServletResponse?response,?Authentication?authentication)?throws?IOException,?ServletException?{
          ????????Msg?result=Msg.success("注銷成功");
          ????????response.setContentType("application/json;charset=utf-8");
          ????????response.getWriter().write(gson.toJson(result));
          ????}
          }


          AccessDeny.java 自定義無權限訪問時的邏輯處理

          //無權訪問
          @Component
          public?class?AccessDeny?implements?AccessDeniedHandler{
          ????@Autowired
          ????Gson?gson;
          ?
          ????@Override
          ????public?void?handle(HttpServletRequest?request,?HttpServletResponse?response,?AccessDeniedException?e)?throws?IOException,?ServletException?{
          ????????Msg?result=?Msg.denyAccess("無權訪問,need?Authorities!!");
          ????????response.setContentType("application/json;charset=utf-8");
          ????????response.getWriter().write(gson.toJson(result));
          ????}
          }


          SessionInformationExpiredStrategy.java 自定義異地登錄、賬號下線時的邏輯處理

          @Component
          public?class?SessionInformationExpiredStrategy?implements?org.springframework.security.web.session.SessionInformationExpiredStrategy{
          ????@Autowired
          ????Gson?gson;
          ?
          ????@Override
          ????public?void?onExpiredSessionDetected(SessionInformationExpiredEvent?event)?throws?IOException,?ServletException?{
          ????????Msg?result=?Msg.fail("您的賬號在異地登錄,建議修改密碼");
          ????????HttpServletResponse?response=event.getResponse();
          ????????response.setContentType("application/json;charset=utf-8");
          ????????response.getWriter().write(gson.toJson(result));
          ????}
          }


          SelfAuthenticationProvider.java 自定義認證邏輯處理

          @Component
          public?class?SelfAuthenticationProvider?implements?AuthenticationProvider{
          ????@Autowired
          ????UserServiceImpl?userServiceImpl;
          ?
          ????@Autowired
          ????BCryptPasswordEncoder?bCryptPasswordEncoder;
          ?
          ????@Override
          ????public?Authentication?authenticate(Authentication?authentication)?throws?AuthenticationException?{
          ????????String?account=?authentication.getName();?????//獲取用戶名
          ????????String?password=?(String)?authentication.getCredentials();??//獲取密碼
          ????????UserDetails?userDetails=?userServiceImpl.loadUserByUsername(account);
          ????????boolean?checkPassword=?bCryptPasswordEncoder.matches(password,userDetails.getPassword());
          ????????if(!checkPassword){
          ????????????throw?new?BadCredentialsException("密碼不正確,請重新登錄!");
          ????????}
          ????????return?new?UsernamePasswordAuthenticationToken(account,password,userDetails.getAuthorities());
          ????}
          ?
          ????@Override
          ????public?boolean?supports(Class?aClass)?{
          ????????return?true;
          ????}
          }


          SpringsecurityConfig.java是springsecurity的配置,詳細解析請看注釋!!

          @EnableWebSecurity
          @EnableGlobalMethodSecurity(prePostEnabled?=?true)?//開啟權限注解,默認是關閉的
          public?class?SpringsecurityConfig?extends?WebSecurityConfigurerAdapter?{
          ?
          ????@Autowired
          ????AuthenticationEnryPoint?authenticationEnryPoint;????//未登錄
          ????@Autowired
          ????AuthenticationSuccess?authenticationSuccess;????//登錄成功
          ????@Autowired
          ????AuthenticationFailure?authenticationFailure;????//登錄失敗
          ????@Autowired
          ????AuthenticationLogout?authenticationLogout;??????//注銷
          ????@Autowired
          ????AccessDeny?accessDeny;??????//無權訪問
          ????@Autowired
          ????SessionInformationExpiredStrategy?sessionInformationExpiredStrategy;????//檢測異地登錄
          ????@Autowired
          ????SelfAuthenticationProvider?selfAuthenticationProvider;??????//自定義認證邏輯處理
          ?
          ????@Bean
          ????public?UserDetailsService?userDetailsService()?{
          ????????return?new?UserServiceImpl();
          ????}
          ?
          ????//加密方式
          ????@Bean
          ????public?BCryptPasswordEncoder?bCryptPasswordEncoder()?{
          ????????return?new?BCryptPasswordEncoder();
          ????}
          ?
          ????//認證
          ????@Override
          ????protected?void?configure(AuthenticationManagerBuilder?auth)?throws?Exception?{
          ????????auth.authenticationProvider(selfAuthenticationProvider);
          ????}
          ?
          ????//授權
          ????@Override
          ????protected?void?configure(HttpSecurity?http)?throws?Exception?{
          ????????//cors()解決跨域問題,csrf()會與restful風格沖突,默認springsecurity是開啟的,所以要disable()關閉一下
          ????????http.cors().and().csrf().disable();?????
          ????????
          ????????//?????/index需要權限為ROLE_USER才能訪問???/hello需要權限為ROLE_ADMIN才能訪問
          ????????http.authorizeRequests()
          ????????????????.antMatchers("/index").hasRole("USER")
          ????????????????.antMatchers("/hello").hasRole("ADMIN")
          ?
          ????????????????
          ????????????????.and()
          ????????????????.formLogin()??//開啟登錄
          ????????????????.permitAll()??//允許所有人訪問
          ????????????????.successHandler(authenticationSuccess)?//?登錄成功邏輯處理
          ????????????????.failureHandler(authenticationFailure)?//?登錄失敗邏輯處理
          ?
          ????????????????.and()
          ????????????????.logout()???//開啟注銷
          ????????????????.permitAll()????//允許所有人訪問
          ????????????????.logoutSuccessHandler(authenticationLogout)?//注銷邏輯處理
          ????????????????.deleteCookies("JSESSIONID")????//刪除cookie
          ?
          ????????????????.and().exceptionHandling()??
          ????????????????.accessDeniedHandler(accessDeny)????//權限不足的時候的邏輯處理
          ????????????????.authenticationEntryPoint(authenticationEnryPoint)??//未登錄是的邏輯處理
          ?
          ????????????????.and()
          ????????????????.sessionManagement()
          ????????????????.maximumSessions(1)?????//最多只能一個用戶登錄一個賬號
          ????????????????.expiredSessionStrategy(sessionInformationExpiredStrategy)??//異地登錄的邏輯處理
          ????????;
          ????}
          }


          application.yml配置文件

          server:
          ??port:?80
          ?
          spring:
          ??datasource:
          ????url:?jdbc:mysql://localhost:3306/springsecurity_test?characterEncoding=utf8&serverTimezone=UTC
          ????username:?root
          ????password:?123456
          ????driver-class-name:?com.mysql.cj.jdbc.Driver


          最終結果在上面就有,源碼地址:??https://gitee.com/liu-wenxin/springsecurity_demo.git??, 使用?git clone?https://gitee.com/liu-wenxin/springsecurity_demo.git??下載到本地。



          版權聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/weixin_42375707/article/details/110678638





          粉絲福利:Java從入門到入土學習路線圖

          ???

          ?長按上方微信二維碼?2 秒


          感謝點贊支持下哈?

          瀏覽 70
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色逼逼视频 | 四虎做爱 | 爆操人妻 | 国产在线第一页 | 国产色色色色 |