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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Cloud E随笔-后端_piece3--实现登录功能

發布時間:2023/12/16 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Cloud E随笔-后端_piece3--实现登录功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 一、spring security 簡介
    • 1. 概要
    • 2. Spring Security 的核心功能
    • 3. Spring Security特點
  • 二、 登錄功能實現
    • 1. 添加pom依賴
    • 2. 修改配置文件 -application.yml
    • 3. 創建工具類
      • 3.1 新建JWTToken工具類
      • 3.2 Admin實現UserDetails類
      • 3.3 添加公共返回對象
      • 3.4 添加登錄相關對象AdminLoginParam
    • 4. 登錄功能實現
      • 4.1 LoginController編寫
      • 4.2 IAdminService接口編寫
      • 4.3 AdminServerImpl接口類的實現
    • 5. Security配置
      • 5.1 準備配置類
      • 5.2 添加jwt 登錄授權攔截器
      • 5.3 添加自定義未授權未登錄結果返回
  • 三、 接口文檔Swagger2準備
    • 1. 依賴添加
    • 2. Swagger2配置
    • 3. 登錄驗證
    • 4. 添加驗證碼模塊
      • 4.1 添加驗證碼依賴
      • 4.2 添加驗證碼配置文件
      • 4.3 驗證碼生成
      • 4.4 修改登錄傳遞參數
    • 5. 登錄成功

一、spring security 簡介

1. 概要

Spring Security是Spring家族中的一員,Security基于Spring框架,提供了一套Web應用安全性的完整解決方案。

2. Spring Security 的核心功能

主要包括:

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

其核心就是一組過濾器鏈,項目啟動后將會自動配置。最核心的就是 Basic Authentication Filter 用來認證用戶的身份,一個在spring security中一種過濾器處理一種認證方式。

3. Spring Security特點

  • 和 Spring 無縫整合。
  • 全面的權限控制。
  • 專門為 Web 開發而設計。
  • 舊版本不能脫離 Web 環境使用。
  • 新版本對整個框架進行了分層抽取,分成了核心模塊和 Web 模塊。單獨引入核心模塊就可以脫離 Web 環境。
  • 重量級(缺點)。

二、 登錄功能實現

項目中使用 Spring Security 框架實現登錄功能

1. 添加pom依賴

<!--security 依賴--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency> <!--JWT 依賴--> <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> </dependency>

2. 修改配置文件 -application.yml

# jwt配置 jwt:# JWT存儲的請求頭tokenHeader: Authorization# JWT 加解密使用的密鑰secret: cloude-secret# JWT的超期限時間(60*60*24)expiration: 604800# JWT 負載中拿到開頭tokenHead: Bearer

3. 創建工具類

3.1 新建JWTToken工具類

創建config.security目錄,并且在其目錄新建JwtTokenUtil.java文件

package com.chuci.server.config.security;import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component;import java.util.Date; import java.util.HashMap; import java.util.Map;/*** @Auther chuci* @Data 2021-12-22 22:43* @Description:*/ @Component public class JwtTokenUtil {private static final String CLAIM_KEY_USERNAME = "sub"; //荷載 用戶名private static final String CLAIM_KEY_CREATED = "created"; //荷載 創建時間// 通過配置獲取@Value("${jwt.secret}")private String secret; //JWT密鑰@Value("${jwt.expiration}")private Long expiration; //JWT失效時間/*** 根據用戶信息生成token* @param userDetails* @return*/public String generateToken(UserDetails userDetails){Map<String, Object> claims = new HashMap<>();claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());claims.put(CLAIM_KEY_CREATED, new Date());return generateToken(claims);}/*** 從token中獲取用戶名* @param token* @return*/public String getUserNameFromToken(String token){String username;try {Claims claims = getClaimsFromToken(token); //根據token獲取荷載username = claims.getSubject();}catch (Exception e){username = null;e.printStackTrace();}return username;}/*** 判斷token是否有效* @param token* @param userDetails* @return*/public boolean validateToken(String token, UserDetails userDetails){String username = getUserNameFromToken(token); // 判斷username是否一致以及token是否失效return username.equals(userDetails.getUsername()) && !isTokenExpired(token);}/*** 驗證token是否可以被刷新* @param token* @return*/public boolean canRefresh(String token){return !isTokenExpired(token); //token過期就可以被刷新了}/*** 刷新token* @param token* @return*/public String refreshToken(String token){Claims claims = getClaimsFromToken(token);claims.put(CLAIM_KEY_CREATED, new Date()); //更新創建時間 達到刷新token的目的return generateToken(claims);}/*** 判斷token是否失效* @param token* @return*/private boolean isTokenExpired(String token) {Date expiredDate = getExpiredDateFromToken(token);return expiredDate.before(new Date());}/*** 從token中獲取時間* @param token* @return*/private Date getExpiredDateFromToken(String token) {Claims claims = getClaimsFromToken(token);return claims.getExpiration();}/*** 從token中獲取荷載* @param token* @return*/private Claims getClaimsFromToken(String token) {Claims claims = null;try {claims = Jwts.parser() //轉荷載.setSigningKey(secret) //添加簽名.parseClaimsJws(token) //添加密鑰.getBody(); //拿到荷載}catch (Exception e){e.printStackTrace();}return claims;}/*** 根據JWT生成token 私有 只需public String generateToken調用* @param claims* @return*/private String generateToken(Map<String, Object> claims){return Jwts.builder() //jwts生成.setClaims(claims) //荷載.setExpiration(generateExporation()) //失效時間.signWith(SignatureAlgorithm.HS512, secret) //簽名.compact(); //密鑰}/*** 生成token失效時間* @return*/private Date generateExporation() { // 系統當前時間 加失效時間return new Date(System.currentTimeMillis() + expiration * 1000);} }

3.2 Admin實現UserDetails類


實現UserDetails,重寫其方法,將所有返回類型改為 true,但注意isEnabled()方法是否啟用賬號,返回值返回Admin類中enable屬性值,因此isEnabled()方法返回值為enabled。

package com.chuci.server.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.util.Collection;import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails;/*** <p>* * </p>** @author chuci* @since 2021-12-22*/ @TableName("t_admin") @ApiModel(value = "Admin對象", description = "") public class Admin implements Serializable, UserDetails {private static final long serialVersionUID = 1L;@ApiModelProperty("id")@TableId(value = "id", type = IdType.AUTO)private Integer id;@ApiModelProperty("姓名")private String name;@ApiModelProperty("手機號碼")private String phone;@ApiModelProperty("住宅電話")private String telephone;@ApiModelProperty("聯系地址")private String address;@ApiModelProperty("是否啟用")private Boolean enabled;@ApiModelProperty("用戶名")private String username;@ApiModelProperty("密碼")private String password;@ApiModelProperty("用戶頭像")private String userFace;@ApiModelProperty("備注")private String remark;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public String getTelephone() {return telephone;}public void setTelephone(String telephone) {this.telephone = telephone;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public Boolean getEnabled() {return enabled;}public void setEnabled(Boolean enabled) {this.enabled = enabled;}public String getUsername() {return username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getUserFace() {return userFace;}public void setUserFace(String userFace) {this.userFace = userFace;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}/*** 重寫 實現 UserDetails* @return*/@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return enabled; //是否啟用}public void setUsername(String username) {this.username = username;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}/*** 重寫toString* @return*/@Overridepublic String toString() {return "Admin{" +"id=" + id +", name=" + name +", phone=" + phone +", telephone=" + telephone +", address=" + address +", enabled=" + enabled +", username=" + username +", password=" + password +", userFace=" + userFace +", remark=" + remark +"}";} }

3.3 添加公共返回對象

每次請求,為了前后端參數統一,規定公共返回對象SysResult
創建vo目錄,并且新建SysResult.java。包含狀態碼code, 返回信息message以及返回對象data。并創建success方法以及error方法。同是使用lombok生成無參全參構造方法以及get/set方法。
注:返回對象可返回任何對象類型。

package com.chuci.server.vo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;/*** 公共返回對象** @Auther chuci* @Data 2022-01-09 22:26* @Description:*/ @Data @NoArgsConstructor @AllArgsConstructor public class SysResult {private long code; //狀態碼private String message; //返回信息private Object data; //返回對象/*** 請求成功返回對象* @return*/public static SysResult success(){return new SysResult(200, "服務請求成功", null);}public static SysResult success(String msg){return new SysResult(200, msg, null);}public static SysResult success(String msg, Object data){return new SysResult(200, msg, data);}/*** 請求失敗* @return*/public static SysResult error(){return new SysResult(500, "服務請求失敗", null);}public static SysResult error(String msg){return new SysResult(500, msg, null);}public static SysResult error(String msg, Object data){return new SysResult(500, msg, data);} }

3.4 添加登錄相關對象AdminLoginParam

創建目錄結構bean,并且新建文件AdminLoginParam.java文件用來登錄時參數傳遞。若使用Admin實體進行登錄實體對象,則傳遞的對象太大,所以在這里進行簡化,暫時僅傳遞 用戶名以及密碼即可(后期還需要傳遞驗證碼)。

package com.chuci.server.bean;import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;/*** 用戶登錄實體類** @Auther chuci* @Data 2022-01-10 15:08* @Description:*/@Data @NoArgsConstructor @AllArgsConstructor @ApiModel(value = "AdminLogin實體類", description = "") public class AdminLoginParam {@ApiModelProperty(value = "用戶名", required = true)private String username;@ApiModelProperty(value = "密碼", required = true)private String password;}

4. 登錄功能實現

4.1 LoginController編寫

新建文件LoginController.java并進行登錄代碼控制層的編寫,主要實現:

  • 用戶登錄
  • 登錄用戶信息獲取
  • 退出登錄
  • package com.chuci.server.controller;import com.chuci.server.entity.Admin; import com.chuci.server.service.IAdminService; import com.chuci.server.bean.AdminLoginParam; import com.chuci.server.vo.SysResult; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest; import java.security.Principal;/*** 登錄** @Auther chuci* @Data 2022-01-10 15:12* @Description:*/ @Api(tags = "LoginController") @RestController public class LoginController {@Autowiredprivate IAdminService adminService;/*** 進行登錄操作,返回SysResult對象。data包含登錄所需驗證* @param adminLoginParam* @param request* @return*/@ApiOperation(value = "登陸之后返回token")@PostMapping("/login")public SysResult login(@RequestBody AdminLoginParam adminLoginParam, HttpServletRequest request){return adminService.login(adminLoginParam.getUsername(), adminLoginParam.getPassword(), adminLoginParam.getCode(), request);}/*** 登錄成功,獲取當前登錄對象所有信息* @param principal* @return*/@ApiOperation(value = "獲取當前登錄用戶信息")@GetMapping("/admin/info")public Admin getAdminInfo(Principal principal){if (principal == null){return null;}String username = principal.getName();Admin admin = adminService.getAdminByUserName(username);admin.setPassword(null);return admin;}/*** 退出登錄,前端刪除token進行退出操作* @return*/@ApiOperation(value = "退出登錄")@PostMapping("/logout")public SysResult logout(){return SysResult.success("注銷成功");}}

    4.2 IAdminService接口編寫

    由MybatisPlus代碼生成器生成的所有Service接口,前綴均有“I”標注,即Admin實體的Service接口為IAdminService。
    主要實現登錄以及登錄用戶信息的查詢。

    public interface IAdminService extends IService<Admin> {/*** 登錄之后返回token** @param code* @param username* @param password* @param request* @return*/SysResult login(String username, String password, HttpServletRequest request);/*** 根據用戶名獲取用戶信息* @param username* @return*/Admin getAdminByUserName(String username); }

    4.3 AdminServerImpl接口類的實現

    package com.chuci.server.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.chuci.server.config.security.JwtTokenUtil; import com.chuci.server.entity.Admin; import com.chuci.server.mapper.AdminMapper; import com.chuci.server.service.IAdminService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.chuci.server.vo.SysResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map;/*** <p>* 服務實現類* </p>** @author chuci* @since 2021-12-22*/ @Service public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Autowiredprivate AdminMapper adminMapper;@Value("${jwt.tokenHead}")private String tokenHead;/*** 登陸之后返回token** @param code* @param username* @param password* @param request* @return*/@Overridepublic SysResult login(String username, String password, String code, HttpServletRequest request) { // 登錄UserDetails userDetails = userDetailsService.loadUserByUsername(username);if (userDetails == null || passwordEncoder.matches(password,userDetails.getPassword())){return SysResult.error("用戶密碼不正確");}if(!userDetails.isEnabled()){return SysResult.error("賬號禁用,請聯系管理員!");}// 更新登錄用戶對象UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);// 生成tokenString token = jwtTokenUtil.generateToken(userDetails);Map<String, String> tokenMap = new HashMap<>();tokenMap.put("token", token);tokenMap.put("tokenHead", tokenHead);return SysResult.success("登錄成功", tokenMap);}/*** 根據用戶名獲取用戶信息* @param username* @return*/@Overridepublic Admin getAdminByUserName(String username) {return adminMapper.selectOne(new QueryWrapper<Admin>().eq("username", username).eq("enabled", true));} }

    5. Security配置

    5.1 準備配置類

    新建文件 SecurityConfig.java
    SpringSecurity的登錄邏輯是通過UserDetailsService中的loadByUserName來實現的。重寫userDetailsService()方法并重新實現configure(AuthenticationManagerBuilder auth)。configure(WebSecurity web)開放部分接口以及資源。configure(HttpSecurity http)使用JWT, 不需要csrf。

    package com.chuci.server.config.security;import com.chuci.server.entity.Admin; import com.chuci.server.service.IAdminService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @Auther chuci* @Data 2022-01-10 22:34* @Description: Security配置類*/ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate IAdminService adminService;@Autowiredprivate RestfulAccessDeniedHandler restfulAccessDeniedHandler;@Autowiredprivate RestAuthorizationEntryPoint restAuthorizationEntryPoint;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());}/*** 開放以下接口網頁資源不進行安全認證* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/login","/logout","/captcha","css/**","js/**","/webjars/**","/swagger-resources/**","/v2/api-docs/**","/index.html","/doc.html","favicon.ico");}@Overrideprotected void configure(HttpSecurity http) throws Exception { // 使用JWT, 不需要csrfhttp.csrf().disable() // 基于token, 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests() // 所有請求都需要認證.anyRequest().authenticated().and().headers().cacheControl(); // 添加jwt 登錄授權攔截器http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class); // 添加自定義未授權未登錄結果返回http.exceptionHandling().accessDeniedHandler(restfulAccessDeniedHandler).authenticationEntryPoint(restAuthorizationEntryPoint);}@Override@Beanpublic UserDetailsService userDetailsService() {return username -> {Admin admin = adminService.getAdminByUserName(username);if (admin != null) {return admin;}return null;};}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic JWTAuthencationTokenFilter jwtAuthencationTokenFilter() {return new JWTAuthencationTokenFilter();} }

    5.2 添加jwt 登錄授權攔截器

    新建JWTAuthencationTokenFilter.java 文件

    package com.chuci.server.config.security;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;/*** @Auther chuci* @Data 2022-01-10 22:59* @Description: JWT 登錄授權過濾器*/public class JWTAuthencationTokenFilter extends OncePerRequestFilter {@Value("${jwt.tokenHeader}")private String tokenHeader;@Value("${jwt.tokenHead}")private String tokenHead;@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String authHeader = request.getHeader(tokenHeader); // 存在tokenif(authHeader != null && authHeader.startsWith(tokenHead)){String authToken = authHeader.substring(tokenHead.length());String username = jwtTokenUtil.getUserNameFromToken(authToken); // token存在用戶但未登錄if (username != null && SecurityContextHolder.getContext().getAuthentication() == null){UserDetails userDetails = userDetailsService.loadUserByUsername(username); // 驗證token是否有效,重新設置用戶對象if (jwtTokenUtil.validateToken(authToken, userDetails)){UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authenticationToken);}}}filterChain.doFilter(request, response);} }

    5.3 添加自定義未授權未登錄結果返回

  • 未授權結果返回
  • package com.chuci.server.config.security;import com.chuci.server.vo.SysResult; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component;import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;/*** @Auther chuci* @Data 2022-01-14 22:06* @Description:*/@Component public class RestfulAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {httpServletResponse.setCharacterEncoding("UTF-8");httpServletResponse.setContentType("application/json");PrintWriter writer = httpServletResponse.getWriter();SysResult result = SysResult.error("權限不足,請聯系管理員");result.setCode(403);writer.write(new ObjectMapper().writeValueAsString(result));writer.flush();writer.close();} }
  • 未登錄結果返回
  • package com.chuci.server.config.security;import com.chuci.server.vo.SysResult; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component;import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;/*** @Auther chuci* @Data 2022-01-14 21:42* @Description:*/ @Component public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {httpServletResponse.setCharacterEncoding("UTF-8"); //設置編碼格式httpServletResponse.setContentType("application/json"); //設置傳輸為JSON格式PrintWriter writer = httpServletResponse.getWriter();SysResult result = SysResult.error("尚未登錄,請登錄后再試!");result.setCode(401);writer.write(new ObjectMapper().writeValueAsString(result));writer.flush();writer.close();} }

    三、 接口文檔Swagger2準備

    在團隊開發中,一個好的 API 文檔不但可以減少大量的溝通成本,還可以幫助一位新人快速上手業務。傳統的做法是由開發人員創建一份 RESTful API 文檔來記錄所有的接口細節,并在程序員之間代代相傳。

    這種做法存在以下幾個問題:

    • API 接口眾多,細節復雜,需要考慮不同的HTTP請求類型、HTTP頭部信息、HTTP請求內容等,想要高質量的完成這份文檔需要耗費大量的精力;

    • 難以維護。隨著需求的變更和項目的優化、推進,接口的細節在不斷地演變,接口描述文檔也需要同步修訂,可是文檔和代碼處于兩個不同的媒介,除非有嚴格的管理機制,否則很容易出現文檔、接口不一致的情況

    Swagger2 的出現就是為了從根本上解決上述問題。它作為一個規范和完整的框架,可以用于生成、描述、調用和可視化 RESTful 風格的 Web 服務:

  • 接口文檔在線自動生成,文檔隨接口變動實時更新,節省維護成本

  • 支持在線接口測試,不依賴第三方工具

  • 1. 依賴添加

    Swagger2自帶UI不太好看,之后更換第三方UI界面

    <!-- swagger2 依賴 --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><!-- Swagger第三方ui依賴 --> <!-- 太丑了,顏色太亮--> <!-- <dependency>--> <!-- <groupId>com.github.xiaoymin</groupId>--> <!-- <artifactId>swagger-bootstrap-ui</artifactId>--> <!-- <version>1.9.6</version>--> <!-- </dependency>--><!--解決集成knife4j時沖突問題--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.0.1-jre</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-micro-spring-boot-starter</artifactId><version>2.0.5</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>2.0.5</version></dependency>

    2. Swagger2配置

    在config文件夾下創建新文件夾swagger存放Swagger2配置類。新建SwaggerConfig.java文件

    在這里進行簡單常用配置

    package com.chuci.server.config.swagger;import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import org.checkerframework.checker.units.qual.A; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;import java.util.ArrayList; import java.util.List;/*** @Auther chuci* @Data 2022-01-14 22:22* @Description:*/ @Configuration @EnableSwagger2 //啟用Swagger2 @EnableKnife4j //啟用Knife4j public class SwaggerConfig {/*** 通過@Configuration注解,讓Spring來加載該類配置。* 再通過@EnableSwagger2注解來啟用Swagger2。* <p>* 再通過createRestApi()函數創建Docket的Bean之后,* apiInfo()用來創建該Api的基本信息(這些基本信息會展現在文檔頁面中)。* select()函數返回一個ApiSelectorBuilder實例用來控制哪些接口暴露給Swagger來展現* 采用掃描所有定義,Swagger會掃描所有Controller定義的API,并產生文檔內容(除了被@ApiIgnore指定的請求)。** @return*/@Beanpublic Docket createRestApi() {return new Docket(DocumentationType.SWAGGER_2) //標明文檔類型 Swagger2.apiInfo(apiInfo()) //apiInfo()用來創建Api的基本信息(這些信息會展現在文檔頁面中).groupName("CloudE_Server") //組名.select() //select()函數返回一個ApiSelectorBuilder實例用來控制哪些接口暴露給Swagger來展現.apis(RequestHandlerSelectors.basePackage("com.chuci.server.controller")) //掃描特定包下面的文件 還有.any 掃描所有包.paths(PathSelectors.any()) //Swagger會掃描該包下的所有Controller定義的API,并產生文檔內容(除了被@ApiIgnore定義的請求).build().securityContexts(securityContexts()).securitySchemes(securitySchemes());}private List<? extends SecurityScheme> securitySchemes() { // 設置請求頭信息List<ApiKey> res = new ArrayList<>();ApiKey apiKey = new ApiKey("Authorization", "Authorization", "Header");res.add(apiKey);return res;}private List<SecurityContext> securityContexts() { // 設置需要認證的路徑List<SecurityContext> res = new ArrayList<>();res.add(getContextByPath("/hello/.*"));return res;}private SecurityContext getContextByPath(String pathRegex) {return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.regex(pathRegex)).build();}private List<SecurityReference> defaultAuth() {List<SecurityReference> res = new ArrayList<>();AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];authorizationScopes[0] = authorizationScope;res.add(new SecurityReference("Authorization", authorizationScopes));return res;}private ApiInfo apiInfo() {return new ApiInfoBuilder().title("CloudE 接口文檔").description("物華天寶 , 龍光射牛斗之墟 \r"+ "人杰地靈 , 徐孺下陳蕃之榻\r"+ "------CloudE 接口文檔").termsOfServiceUrl("www.baidu.com").contact(new Contact("【楚辭】", "http://localhost:8081/doc.html", "自己郵箱地址")).version("1.0").build();} }

    同時為了測試,我們新建了HelloController。java文件進行Swagger的測試
    我們通過域名:端口號/doc,html來訪問swagger

    3. 登錄驗證

    未登錄狀態下去請求接口:

    提示未登錄,需要登錄。

    4. 添加驗證碼模塊

    4.1 添加驗證碼依賴

    這里采用谷歌的驗證碼解決方案

    </dependency><!-- google kaptcha依賴 --><dependency><groupId>com.github.axet</groupId><artifactId>kaptcha</artifactId><version>0.0.9</version></dependency>

    4.2 添加驗證碼配置文件


    新建CaptchaConfig配置文件,在這里進行驗證碼的一些設置,如邊框,字體大小,字體樣式等等。

    package com.chuci.server.config.captcha;import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import java.util.Properties;/*** 驗證碼配置類** @Auther chuci* @Data 2022-01-15 18:04* @Description:*/ @Configuration public class CaptchaConfig {@Beanpublic DefaultKaptcha defaultKaptcha() {//驗證碼生成器DefaultKaptcha defaultKaptcha = new DefaultKaptcha();//配置Properties properties = new Properties();//是否有邊框properties.setProperty("kaptcha.border", "yes");//設置邊框顏色properties.setProperty("kaptcha.border.color", "105,179,90");//邊框粗細度,默認為1// properties.setProperty("kaptcha.border.thickness","1");//驗證碼properties.setProperty("kaptcha.session.key", "code");//驗證碼文本字符顏色 默認為黑色properties.setProperty("kaptcha.textproducer.font.color", "blue");//設置字體樣式properties.setProperty("kaptcha.textproducer.font.names", "宋體,楷體,微軟雅黑");//字體大小,默認40properties.setProperty("kaptcha.textproducer.font.size", "30");//驗證碼文本字符內容范圍 默認為abced2345678gfynmnpwx// properties.setProperty("kaptcha.textproducer.char.string", "");//字符長度,默認為5properties.setProperty("kaptcha.textproducer.char.length", "4");//字符間距 默認為2properties.setProperty("kaptcha.textproducer.char.space", "4");//驗證碼圖片寬度 默認為200properties.setProperty("kaptcha.image.width", "100");//驗證碼圖片高度 默認為40properties.setProperty("kaptcha.image.height", "40");Config config = new Config(properties);defaultKaptcha.setConfig(config);return defaultKaptcha;}}

    4.3 驗證碼生成

    package com.chuci.server.controller;import com.google.code.kaptcha.impl.DefaultKaptcha; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; import java.io.IOException;/*** 驗證碼** @Auther chuci* @Data 2022-01-15 18:14* @Description:*/ @RestController public class CaptchaController {@Autowiredprivate DefaultKaptcha defaultKaptcha;@ApiOperation(value = "驗證碼")@GetMapping(value = "/captcha", produces = "image/jpeg")public void captcha(HttpServletRequest request, HttpServletResponse response){// 定義response輸出類型為image/jpeg類型response.setDateHeader("Expires", 0);// Set standard HTTP/1.1 no-cache headers.response.setHeader("Cache-Control", "no-store, no-cache, mustrevalidate");// Set IE extended HTTP/1.1 no-cache headers (use addHeader).response.addHeader("Cache-Control", "post-check=0, pre-check=0");// Set standard HTTP/1.0 no-cache header.response.setHeader("Pragma", "no-cache");// return a jpegresponse.setContentType("image/jpeg");//-------------------生成驗證碼 begin --------------------------//獲取驗證碼文本內容String text = defaultKaptcha.createText();System.out.println("驗證碼:" + text);//將驗證碼放在session中request.getSession().setAttribute("captcha", text);//根據文本內容創建圖片驗證碼BufferedImage image = defaultKaptcha.createImage(text);ServletOutputStream outputStream = null;try {outputStream = response.getOutputStream();//輸出流輸出圖片,格式jpgImageIO.write(image, "jpg", outputStream);outputStream.flush();} catch (IOException e) {e.printStackTrace();}finally {if(outputStream != null){try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}//-------------------生成驗證碼 end --------------------------} }

    重啟項目,我們可以看到swagger出現驗證碼模塊,并且測試成功生成驗證碼


    4.4 修改登錄傳遞參數

  • 修改AdminLoginParam文件,新增驗證碼


  • 修改登錄Controller文件,傳遞參數新增驗證碼部分


    同時修改Service層文件以及實現Imp層文件,并進行驗證碼正確性的檢測

  • // 驗證碼檢測String captcha = request.getSession().getAttribute("captcha").toString();System.out.println("captcha:" + captcha + "; code:" + code);if(!StringUtils.hasLength(code) || !captcha.equalsIgnoreCase(code)){return SysResult.error("驗證碼錯誤,請重新輸入!");}

    5. 登錄成功

  • 驗證碼錯誤
  • 登錄成功
  • 登錄 成功,將返回的data信息中 tokenHead、token分別粘貼到swagger中Authorize頁面參數值輸入框中作為登錄token令牌,tokenHead與token之間使用空格隔開
  • "tokenHead": "Bearer" "token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImNyZWF0ZWQiOjE2NDI2NjUwMzc4OTYsImV4cCI6MTY0MzI2OTgzN30.iVkznvdJ3DCCVSdVbCBN34nz_BG1JGXyfolJ5GLH7_uIZvEcMHLAzc6q8Hkzqx8AX_d7VEH_wk20mrbtk3HvgA"


    再次進行接口調用,則會帶如剛剛的參數值token令牌進行登錄驗證。



    致此,整個登錄模塊已完成,等待前端頁面調用驗證即可。


    至此,本節完~~~

    上一節: Cloud E隨筆-后端_piece2–代碼生成器
    下一節:

    此系列以完整記錄自己項目經歷此系列以完整記錄自己項目經歷

    總結

    以上是生活随笔為你收集整理的Cloud E随笔-后端_piece3--实现登录功能的全部內容,希望文章能夠幫你解決所遇到的問題。

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