日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring-security-学习笔记-02-基于Session的认证方式

發(fā)布時間:2024/7/19 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring-security-学习笔记-02-基于Session的认证方式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

spring-security-學習筆記-02-基于Session的認證方式

文章目錄

  • spring-security-學習筆記-02-基于Session的認證方式
  • 2 基于Session的認證方式
    • 2.1 認證流程
    • 2.2.創(chuàng)建工程
      • 2.2.1 創(chuàng)建maven工程
      • 2.2.2 Spring 容器配置
      • 2.2.3 servletContext配置
      • 2.2.4 加載 Spring容器
    • 2.3.實現認證功能
      • 2.3.1 認證頁面
      • 2.3.2 認證接口
    • 2.4.實現會話功能
    • 2.5.實現授權功能
    • 2.6 小結

2 基于Session的認證方式

2.1 認證流程

??基于Session認證方式的流程是,用戶認證成功后,在服務端生成用戶相關的數據保存在session(當前會話),而發(fā) 給客戶端的 sesssion_id 存放到 cookie 中,這樣用客戶端請求時帶上 session_id 就可以驗證服務器端是否存在 session 數據,以此完成用戶的合法校驗。當用戶退出系統(tǒng)或session過期銷毀時,客戶端的session_id也就無效了。
??下圖是session認證方式的流程圖:

??基于Session的認證機制由Servlet規(guī)范定制,Servlet容器已實現,用戶通過HttpSession的操作方法即可實現,如 下是HttpSession相關的操作API。

2.2.創(chuàng)建工程

本案例工程使用maven進行構建,使用SpringMVC、Servlet3.0實現。

2.2.1 創(chuàng)建maven工程

創(chuàng)建maven工程 security-springmvc,工程結構如下:

pom文件配置

2.2.2 Spring 容器配置

在config包下定義ApplicationConfig.java,它對應web.xml中ContextLoaderListener的配置

@Configuration @ComponentScan(basePackages = "com.itheima.security.springmvc" // 排除掉這個注解@ComponentScan,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)}) public class ApplicationConfig { //在此配置除了Controller的其它bean,比如:數據庫鏈接池、事務管理器、業(yè)務bean等。}

2.2.3 servletContext配置

??本案例采用Servlet3.0無web.xml方式,的config包下定義WebConfig.java,它對應于DispatcherServlet配 置。

@Configuration//就相當于springmvc.xml文件 @EnableWebMvc @ComponentScan(basePackages = "com.itheima.security.springmvc",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)}) public class WebConfig implements WebMvcConfigurer {@AutowiredSimpleAuthenticationInterceptor simpleAuthenticationInterceptor;//視頻解析器@Beanpublic InternalResourceViewResolver viewResolver(){InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/view/");viewResolver.setSuffix(".jsp");return viewResolver;}@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");} }

2.2.4 加載 Spring容器

??在init包下定義Spring容器初始化類SpringApplicationInitializer,此類實現WebApplicationInitializer接口, Spring容器啟動時加載WebApplicationInitializer接口的所有實現類。

public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {//spring容器,相當于加載 applicationContext.xml@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{ApplicationConfig.class};}//servletContext,相當于加載springmvc.xml@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class};}//url-mapping@Overrideprotected String[] getServletMappings() {return new String[]{"/"};} }

2.3.實現認證功能

2.3.1 認證頁面

在webapp/WEB-INF/views下定義認證頁面login.jsp,本案例只是測試認證流程,頁面沒有添加css樣式,頁面實 現可填入用戶名,密碼,觸發(fā)登錄將提交表單信息至/login,內容如下:

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %> <html> <head><title>用戶登錄</title> </head> <body> <form action="login" method="post">用戶名:<input type="text" name="username"><br>&nbsp;&nbsp;&nbsp;碼:<input type="password" name="password"><br><input type="submit" value="登錄"> </form> </body> </html>

在WebConfig中新增如下配置,將/直接導向login.jsp頁面:

@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");}

啟動項目,訪問/路徑地址,進行測試

2.3.2 認證接口

用戶進入認證頁面,輸入賬號和密碼,點擊登錄,請求/login進行身份認證。

(1)定義認證接口,此接口用于對傳來的用戶名、密碼校驗,若成功則返回該用戶的詳細信息,否則拋出錯誤異常:

/*** 認證服務*/ @Data public interface AuthenticationService {/*** 用戶認證* @param authenticationRequest 用戶認證請求,賬號和密碼* @return 認證成功的用戶信息*/UserDto authentication(AuthenticationRequest authenticationRequest); }

認證請求結構

@Data public class AuthenticationRequest {//認證請求參數,賬號、密碼。。/*** 用戶名*/private String username;/*** 密碼*/private String password;}

認證成功后返回的用戶詳細信息,也就是當前登錄用戶的信息:

@Data @AllArgsConstructor public class UserDto {public static final String SESSION_USER_KEY = "_user";//用戶身份信息private String id;private String username;private String password;private String fullname;private String mobile;/*** 用戶權限*/private Set<String> authorities; }

(2)認證實現類,根據用戶名查找用戶信息,并校驗密碼,這里模擬了兩個用戶:

@Service public class AuthenticationServiceImpl implements AuthenticationService{/*** 用戶認證,校驗用戶身份信息是否合法** @param authenticationRequest 用戶認證請求,賬號和密碼* @return 認證成功的用戶信息*/@Overridepublic UserDto authentication(AuthenticationRequest authenticationRequest) {//校驗參數是否為空if(authenticationRequest == null|| StringUtils.isEmpty(authenticationRequest.getUsername())|| StringUtils.isEmpty(authenticationRequest.getPassword())){throw new RuntimeException("賬號和密碼為空");}//根據賬號去查詢數據庫,這里測試程序采用模擬方法UserDto user = getUserDto(authenticationRequest.getUsername());//判斷用戶是否為空if(user == null){throw new RuntimeException("查詢不到該用戶");}//校驗密碼if(!authenticationRequest.getPassword().equals(user.getPassword())){throw new RuntimeException("賬號或密碼錯誤");}//認證通過,返回用戶身份信息return user;}//根據賬號查詢用戶信息private UserDto getUserDto(String userName){return userMap.get(userName);}//用戶信息private Map<String,UserDto> userMap = new HashMap<>();{Set<String> authorities1 = new HashSet<>();authorities1.add("p1");//這個p1我們人為讓它和/r/r1對應Set<String> authorities2 = new HashSet<>();authorities2.add("p2");//這個p2我們人為讓它和/r/r2對應userMap.put("zhangsan",new UserDto("1010","zhangsan","123","張三","133443",authorities1));userMap.put("lisi",new UserDto("1011","lisi","456","李四","144553",authorities2));} }

(3)登錄Controller,對/login請求處理,它調用AuthenticationService完成認證并返回登錄結果提示信息:

@RestController public class LoginController {@AutowiredAuthenticationService authenticationService;/*** 用戶登錄 * @param authenticationRequest 登錄請求 * @return * */@RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")public String login(AuthenticationRequest authenticationRequest, HttpSession session){UserDto userDto = authenticationService.authentication(authenticationRequest);//存入sessionsession.setAttribute(UserDto.SESSION_USER_KEY,userDto);return userDto.getUsername() +"登錄成功";}}

2.4.實現會話功能

??會話是指用戶登入系統(tǒng)后,系統(tǒng)會記住該用戶的登錄狀態(tài),他可以在系統(tǒng)連續(xù)操作直到退出系統(tǒng)的過程。
??認證的目的是對系統(tǒng)資源的保護,每次對資源的訪問,系統(tǒng)必須得知道是誰在訪問資源,才能對該請求進行合法性 攔截。因此,在認證成功后,一般會把認證成功的用戶信息放入Session中,在后續(xù)的請求中,系統(tǒng)能夠從Session 中獲取到當前用戶,用這樣的方式來實現會話機制。

(1)增加會話控制
首先在UserDto中定義一個SESSION_USER_KEY,作為Session中存放登錄用戶信息的key。

public static final String SESSION_USER_KEY = "_user";

??然后修改LoginController,認證成功后,將用戶信息放入當前會話。并增加用戶登出方法,登出時將session置為 失效。

@RestController public class LoginController {@AutowiredAuthenticationService authenticationService;/*** 用戶登錄 * @param authenticationRequest 登錄請求 * @return * */@RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")public String login(AuthenticationRequest authenticationRequest, HttpSession session){UserDto userDto = authenticationService.authentication(authenticationRequest);//存入sessionsession.setAttribute(UserDto.SESSION_USER_KEY,userDto);return userDto.getUsername() +"登錄成功";}/*** 用戶退出 * @param session 當前session對象 * @return * */@GetMapping(value = "/logout",produces = {"text/plain;charset=UTF-8"})public String logout(HttpSession session){session.invalidate();return "退出成功";} }

(2)增加測試資源
??修改LoginController,增加測試資源1,它從當前會話session中獲取當前登錄用戶,并返回提示信息給前臺。

@GetMapping(value = "/r/r1",produces = {"text/plain;charset=UTF-8"})public String r1(HttpSession session){String fullname = null;Object object = session.getAttribute(UserDto.SESSION_USER_KEY);if(object == null){fullname = "匿名";}else{UserDto userDto = (UserDto) object;fullname = userDto.getFullname();}return fullname+"訪問資源r1";}@GetMapping(value = "/r/r2",produces = {"text/plain;charset=UTF-8"})public String r2(HttpSession session){String fullname = null;Object userObj = session.getAttribute(UserDto.SESSION_USER_KEY);if(userObj != null){fullname = ((UserDto)userObj).getFullname();}else{fullname = "匿名";}return fullname + " 訪問資源2";}




??測試結果說明,在用戶登錄成功時,該用戶信息已被成功放入session,并且后續(xù)請求可以正常從session中獲取當 前登錄用戶信息,符合預期結果。

2.5.實現授權功能

??現在我們已經完成了用戶身份憑證的校驗以及登錄的狀態(tài)保持,并且我們也知道了如何獲取當前登錄用戶(從 Session中獲取)的信息,接下來,用戶訪問系統(tǒng)需要經過授權,即需要完成如下功能:

  • 匿名用戶(未登錄用戶)訪問攔截:禁止匿名用戶訪問某些資源。
  • 登錄用戶訪問攔截:根據用戶的權限決定是否能訪問某些資源。

(1)增加權限數據
??為了實現這樣的功能,我們需要在UserDto里增加權限屬性,用于表示該登錄用戶所擁有的權限,同時修改 UserDto的構造方法。

@Data @AllArgsConstructor public class UserDto {public static final String SESSION_USER_KEY = "_user";//用戶身份信息private String id;private String username;private String password;private String fullname;private String mobile;/*** 用戶權限*/private Set<String> authorities; }

??并在AuthenticationServiceImpl中為模擬用戶初始化權限,其中張三給了p1權限,李四給了p2權限。

//用戶信息private Map<String,UserDto> userMap = new HashMap<>();{Set<String> authorities1 = new HashSet<>();authorities1.add("p1");//這個p1我們人為讓它和/r/r1對應Set<String> authorities2 = new HashSet<>();authorities2.add("p2");//這個p2我們人為讓它和/r/r2對應userMap.put("zhangsan",new UserDto("1010","zhangsan","123","張三","133443",authorities1));userMap.put("lisi",new UserDto("1011","lisi","456","李四","144553",authorities2));}

(2)增加測試資源

(3)實現授權攔截器
??在interceptor包下定義SimpleAuthenticationInterceptor攔截器,實現授權攔截:

  • 校驗用戶是否登錄
  • 校驗用戶是否擁有操作權限
  • @Component public class SimpleAuthenticationInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//在這個方法中校驗用戶請求的url是否在用戶的權限范圍內//取出用戶身份信息Object object = request.getSession().getAttribute(UserDto.SESSION_USER_KEY);if(object == null){//沒有認證,提示登錄writeContent(response,"請登錄");}UserDto userDto = (UserDto) object;//請求的urlString requestURI = request.getRequestURI();if( userDto.getAuthorities().contains("p1") && requestURI.contains("/r/r1")){return true;}if( userDto.getAuthorities().contains("p2") && requestURI.contains("/r/r2")){return true;}writeContent(response,"沒有權限,拒絕訪問");return false;}//響應信息給客戶端private void writeContent(HttpServletResponse response, String msg) throws IOException {response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.print(msg);writer.close();} }

    在WebConfig中配置攔截器,匹配/r/**的資源為受保護的系統(tǒng)資源,訪問該資源的請求進入 SimpleAuthenticationInterceptor攔截器。

    @Autowired SimpleAuthenticationInterceptor simpleAuthenticationInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");}

    2.6 小結

    ??基于Session的認證方式是一種常見的認證方式,至今還有非常多的系統(tǒng)在使用。我們在此小節(jié)使用Spring mvc技 術對它進行簡單實現,旨在讓大家更清晰實在的了解用戶認證、授權以及會話的功能意義及實現套路,也就是它們 分別干了哪些事兒?大概需要怎么做?
    ??而在正式生產項目中,我們往往會考慮使用第三方安全框架(如 spring security,shiro等安全框架)來實現認證 授權功能,因為這樣做能一定程度提高生產力,提高軟件標準化程度,另外往往這些框架的可擴展性考慮的非常全 面。但是缺點也非常明顯,這些通用化組件為了提高支持范圍會增加很多可能我們不需要的功能,結構上也會比較 抽象,如果我們不夠了解它,一旦出現問題,將會很難定位。

    總結

    以上是生活随笔為你收集整理的spring-security-学习笔记-02-基于Session的认证方式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。