spring security认证
1 開發基于表單的認證
Spring security核心的功能
- 認證(你是誰?)
- 授權(你能干什么?)
- 攻擊防護(防止偽造身份)
spring security實現了默認的用戶名+密碼認證,默認用戶名為user,密碼為:
?
?
?spring security基本原理:過濾器鏈
?
? 對于UsernamePasswordAuthenticationFilter只會攔截 url為/login,method為POST的請求。
?1.1 自定義用戶認證邏輯
1)處理用戶信息獲取邏輯
UserDetailsService接口,只有一個方法:loadUserByUsername
實現該接口:數據庫中存放的是加密密碼,對于同一個密碼不同時間的加密密文不一樣
@Component public class MyUserDetailsService implements UserDetailsService {private Logger logger = LoggerFactory.getLogger(getClass());@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {logger.info("用戶名信息:" + s);// 根據用戶名查找用戶信息logger.info("數據庫密碼:" + passwordEncoder.encode("123456"));// 用戶名和密碼信息用來做認證,權限信息用來對該用戶做授權return new User(s, "$2a$10$eFw06n0ABK2NFuse8y5f/eDUq7we26qQTceEtXSWNbMXnQ5Yf5Iha",AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));} }?
?
2)處理用戶信息校驗邏輯
?
處理密碼加密解密:在配置文件中將PasswordEncoder對象注入spring容器,等價于@Component+包掃描組件
?
?
1.2 個性化用戶認證流程
1)對于瀏覽器,返回自定義登錄頁面,讓UsernamePasswordXxxFilter來處理登錄請求;對于調用RESTful服務,返回json錯誤信息。
? 用戶登錄成功后 ,對于瀏覽器,返回需要的頁面;對于服務,返回json數據。
?
? 權限配置:
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()// 表單登錄.loginPage("/authentication/require") //將登錄頁面重定向到controller.loginProcessingUrl("/authentication/form").and().authorizeRequests() //請求授權.antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll()//該頁面允許通過 .anyRequest().authenticated() // 其他資源需要認證 .and().csrf().disable(); // 將跨站防護關掉}?
控制器,根據之前URL的路徑判斷是否為RESTful服務,在處理
/* 當客戶端發出請求,當需要認證時,spring security會重定向到該控制器*/ @RestController public class BrowserSecurityController {private Logger logger = LoggerFactory.getLogger(getClass());// 請求緩存private RequestCache requestCache = new HttpSessionRequestCache();private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();@Autowiredprivate SecurityProperties securityProperties;/*** 當需要身份認證時跳轉到這里* @param request* @param response* @return*/@RequestMapping("/authentication/require")@ResponseStatus(code = HttpStatus.UNAUTHORIZED)public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {// 判斷請求類型,HTML或者appSavedRequest savedRequest = requestCache.getRequest(request, response);if(savedRequest!=null){String targetUrl = savedRequest.getRedirectUrl();logger.info("引發跳轉的URL:"+targetUrl);// 如果之前的URL為.html結尾的URL,則重定向到登錄頁面if(StringUtils.endsWithIgnoreCase(targetUrl, ".html")){redirectStrategy.sendRedirect(request, response,securityProperties.getBrowser().getLoginPage());}}return new SimpleResponse("請求的服務需要身份認證,請引導用戶到登錄頁面");} } BrowserSecurityController.java?
?
在啟動項目中的application.properties文件中配置登錄頁面:
# 配置登錄頁面 getword.security.browser.loginPage=/demo.html?
?
讀取配置文件信息:
import org.springframework.boot.context.properties.ConfigurationProperties;// 讀取前綴為getword.security的屬性配置,其中browser中的屬性會被讀取到browserProperties中 @ConfigurationProperties(prefix = "getword.security") public class SecurityProperties {// browser的屬性會匹配getword.security.browser后面的屬性private BrowserProperties browser = new BrowserProperties();public BrowserProperties getBrowser() {return browser;}public void setBrowser(BrowserProperties browser) {this.browser = browser;} } SecurityProperties.java
?
?
public class BrowserProperties {private String loginPage = "/login.html"; //默認值public String getLoginPage() {return loginPage;}public void setLoginPage(String loginPage) {this.loginPage = loginPage;} } BrowserProperties.java?
?
@Configuration @EnableConfigurationProperties(SecurityProperties.class) //讓屬性配置讀取器生效 public class SecurityCodeConfig { }?
?
2)自定義登錄成功處理,異步登錄,AuthenticationSuccessHandler接口
自定義登錄成處理:
@Component("vstudyAuthenticationSuccessHandler") public class VstudyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {private Logger logger = LoggerFactory.getLogger(getClass());//工具類, 將對象轉成json @Autowiredprivate ObjectMapper objectMapper;// 登錄成功后調用 @Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {logger.info("登錄成功");response.setContentType("application/json;charset=utf-8");response.getWriter().write(objectMapper.writeValueAsString(authentication));} } VstudyAuthenticationSuccessHandler注冊,使處理器生效:
?
?
3)登錄失敗處理
@Component("vstudyAuthenticationFailHandler") public class VstudyAuthenticationFailHandler implements AuthenticationFailureHandler {@Autowiredprivate ObjectMapper objectMapper;private Logger logger = LoggerFactory.getLogger(getClass());@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,AuthenticationException e) throws IOException, ServletException {logger.info("登錄失敗");response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); //服務器內部錯誤response.setContentType("application/json;charset=utf-8");response.getWriter().write(objectMapper.writeValueAsString(e));} } VstudyAuthenticationFailHandler.java?
配置:和success類似
?
4)判斷請求方式,做出相應的處理
successHandler:
@Component("vstudyAuthenticationSuccessHandler") public class VstudyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {private Logger logger = LoggerFactory.getLogger(getClass());//工具類, 將對象轉成json @Autowiredprivate ObjectMapper objectMapper;@Autowiredprivate SecurityProperties securityProperties; //獲取配置信息// 登錄成功后調用 @Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {logger.info("登錄成功");if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){response.setContentType("application/json;charset=utf-8");response.getWriter().write(objectMapper.writeValueAsString(authentication));}else{// 調用父類方法,完成重定向跳轉super.onAuthenticationSuccess(request, response, authentication);}} } VstudyAuthenticationSuccessHandler?
failureHandler:
@Component("vstudyAuthenticationFailHandler") public class VstudyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {@Autowiredprivate SecurityProperties securityProperties;@Autowiredprivate ObjectMapper objectMapper;private Logger logger = LoggerFactory.getLogger(getClass());@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,AuthenticationException e) throws IOException, ServletException {logger.info("登錄失敗");if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); //服務器內部錯誤response.setContentType("application/json;charset=utf-8");response.getWriter().write(objectMapper.writeValueAsString(e));}else{super.onAuthenticationFailure(request, response, e);}} } VstudyAuthenticationFailHandler?
?
2 認證流程
?
?
3 圖形驗證碼
3.1 生成圖形驗證碼
驗證碼圖片信息:
public class ImageCode {private BufferedImage image;private String code;private LocalDateTime expireTime;//過期時間public ImageCode(BufferedImage image, String code, int expireIn){this.image = image;this.code = code;this.expireTime = LocalDateTime.now().plusSeconds(expireIn);}public ImageCode(BufferedImage image, String code, LocalDateTime expireTime){this.image = image;this.code = code;this.expireTime = expireTime;} } ImageCode?
?
控制器:
@RestController public class ValidateCodeController {public static String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();@Autowiredprivate SecurityProperties securityProperties;@GetMapping("/image/code")public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {ImageCode imageCode = createImageCode(new ServletWebRequest(request,response));sessionStrategy.setAttribute(new ServletWebRequest(request, response), SESSION_KEY, imageCode);ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());}/*** 生成ImageCode驗證碼* @param request* @return*/public ImageCode createImageCode(ServletWebRequest request){// 生成驗證碼,方法很多int width = 60;int height = 20;BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = image.getGraphics();Random random = new Random();g.setColor(getRandColor(200, 250));g.fillRect(0, 0, width, height);g.setFont(new Font("Times New Roman", Font.ITALIC, 20));g.setColor(getRandColor(160, 200));for (int i = 0; i < 155; i++) {int x = random.nextInt(width);int y = random.nextInt(height);int xl = random.nextInt(12);int yl = random.nextInt(12);g.drawLine(x, y, x + xl, y + yl);}String sRand = "";for (int i = 0; i < 4; i++) {String rand = String.valueOf(random.nextInt(10));sRand += rand;g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));g.drawString(rand, 13 * i + 6, 16);}g.dispose();return new ImageCode(image, sRand, 100);}/*** 生成隨機背景條紋** @param fc* @param bc* @return*/private Color getRandColor(int fc, int bc) {Random random = new Random();if (fc > 255) {fc = 255;}if (bc > 255) {bc = 255;}int r = fc + random.nextInt(bc - fc);int g = fc + random.nextInt(bc - fc);int b = fc + random.nextInt(bc - fc);return new Color(r, g, b);} } ValidateCodeController?
?
3.2 驗證碼校驗
?自定義過濾器:
public class ValidateCodeFilter extends OncePerRequestFilter {/*** 驗證碼校驗失敗處理器*/private AuthenticationFailureHandler authenticationFailureHandler;/*** 系統配置信息*/private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();/*** 系統中的校驗碼處理器*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 只處理登錄請求if (StringUtils.equals("/authetication/form", request.getRequestURI())&& StringUtils.equalsIgnoreCase(request.getMethod(), "POST")) {try {logger.info("驗證碼校驗通過");} catch (ValidateCodeException e) {//驗證失敗 authenticationFailureHandler.onAuthenticationFailure(request, response, e);}}filterChain.doFilter(request, response);}protected void validate(ServletWebRequest request) throws ServletRequestBindingException {// 從session中拿到imageCodeImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);// 獲取客戶端輸入的code,當前請求參數String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");if(StringUtils.isBlank(codeInRequest)){throw new ValidateCodeException("驗證碼不能為空");}if(codeInSession==null){throw new ValidateCodeException("驗證碼不存在");}if(codeInSession.isExpired()){throw new ValidateCodeException("驗證碼已過期");}if(!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)){throw new ValidateCodeException("驗證碼不匹配");}sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);}public AuthenticationFailureHandler getAuthenticationFailureHandler() {return authenticationFailureHandler;}public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {this.authenticationFailureHandler = authenticationFailureHandler;} } ValidateCodeFilter?
?
配置:
?
執行流程:
?/index.html ->redirect ->/authentication/require(控制器,判斷是.html結尾)->login.html ->ValidateCodeFilter ->exception ->?VstudyAuthenticationFailHandler ->loginType:JSON
?
login.html中,使用ajax發送登錄請求 -> 驗證碼過濾器通過 -> UsernamePasswordFilter通過 -> 返回登錄結果信息
?
3.3 驗證碼的接口
- 為了方便修改驗證碼的參數,如寬度、高度、長度等信息,我們將通過配置文件的形式配置這些信息。還有驗證碼的URL地址。
- 驗證碼攔截的接口可配置,比如為了限制用戶操作頻率,對用戶操作使用驗證碼進行限制。驗證碼過濾器可以攔截多個控制器請求。
- 驗證碼的生成邏輯可以配置
三級配置:
?
?1)驗證碼參數:
默認配置:
/*** 圖形驗證碼*/ public class ImageCodeProperties {private int width = 67;private int height = 23;private int len = 4;private int expireIn = 60; //60秒后過期 //seter getter }?
?驗證碼信息:
/*** 驗證碼:包括圖形驗證碼、短信驗證碼*/ public class ValidateCodeProperties {private ImageCodeProperties image; }?
?屬性讀取:
@ConfigurationProperties(prefix = "getword.security") public class SecurityProperties {// browser的屬性會匹配getword.security.browser后面的屬性private BrowserProperties browser = new BrowserProperties();// 驗證碼屬性匹配getword.security.code后面的屬性private ValidateCodeProperties code = new ValidateCodeProperties(); }?
?
?
?
?
?
end
轉載于:https://www.cnblogs.com/zhuxiang1633/p/10311320.html
總結
以上是生活随笔為你收集整理的spring security认证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 代码转程序_精悍的Pyth
- 下一篇: VBoxGuestAdditions加载