spring security认证
1 開發(fā)基于表單的認(rèn)證
Spring security核心的功能
- 認(rèn)證(你是誰(shuí)?)
- 授權(quán)(你能干什么?)
- 攻擊防護(hù)(防止偽造身份)
spring security實(shí)現(xiàn)了默認(rèn)的用戶名+密碼認(rèn)證,默認(rèn)用戶名為user,密碼為:
?
?
?spring security基本原理:過(guò)濾器鏈
?
? 對(duì)于UsernamePasswordAuthenticationFilter只會(huì)攔截 url為/login,method為POST的請(qǐng)求。
?1.1 自定義用戶認(rèn)證邏輯
1)處理用戶信息獲取邏輯
UserDetailsService接口,只有一個(gè)方法:loadUserByUsername
實(shí)現(xiàn)該接口:數(shù)據(jù)庫(kù)中存放的是加密密碼,對(duì)于同一個(gè)密碼不同時(shí)間的加密密文不一樣
@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);// 根據(jù)用戶名查找用戶信息logger.info("數(shù)據(jù)庫(kù)密碼:" + passwordEncoder.encode("123456"));// 用戶名和密碼信息用來(lái)做認(rèn)證,權(quán)限信息用來(lái)對(duì)該用戶做授權(quán)return new User(s, "$2a$10$eFw06n0ABK2NFuse8y5f/eDUq7we26qQTceEtXSWNbMXnQ5Yf5Iha",AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));} }?
?
2)處理用戶信息校驗(yàn)邏輯
?
處理密碼加密解密:在配置文件中將PasswordEncoder對(duì)象注入spring容器,等價(jià)于@Component+包掃描組件
?
?
1.2 個(gè)性化用戶認(rèn)證流程
1)對(duì)于瀏覽器,返回自定義登錄頁(yè)面,讓UsernamePasswordXxxFilter來(lái)處理登錄請(qǐng)求;對(duì)于調(diào)用RESTful服務(wù),返回json錯(cuò)誤信息。
? 用戶登錄成功后 ,對(duì)于瀏覽器,返回需要的頁(yè)面;對(duì)于服務(wù),返回json數(shù)據(jù)。
?
? 權(quán)限配置:
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()// 表單登錄.loginPage("/authentication/require") //將登錄頁(yè)面重定向到controller.loginProcessingUrl("/authentication/form").and().authorizeRequests() //請(qǐng)求授權(quán).antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll()//該頁(yè)面允許通過(guò) .anyRequest().authenticated() // 其他資源需要認(rèn)證 .and().csrf().disable(); // 將跨站防護(hù)關(guān)掉}?
控制器,根據(jù)之前URL的路徑判斷是否為RESTful服務(wù),在處理
/* 當(dāng)客戶端發(fā)出請(qǐng)求,當(dāng)需要認(rèn)證時(shí),spring security會(huì)重定向到該控制器*/ @RestController public class BrowserSecurityController {private Logger logger = LoggerFactory.getLogger(getClass());// 請(qǐng)求緩存private RequestCache requestCache = new HttpSessionRequestCache();private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();@Autowiredprivate SecurityProperties securityProperties;/*** 當(dāng)需要身份認(rèn)證時(shí)跳轉(zhuǎn)到這里* @param request* @param response* @return*/@RequestMapping("/authentication/require")@ResponseStatus(code = HttpStatus.UNAUTHORIZED)public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {// 判斷請(qǐng)求類型,HTML或者appSavedRequest savedRequest = requestCache.getRequest(request, response);if(savedRequest!=null){String targetUrl = savedRequest.getRedirectUrl();logger.info("引發(fā)跳轉(zhuǎn)的URL:"+targetUrl);// 如果之前的URL為.html結(jié)尾的URL,則重定向到登錄頁(yè)面if(StringUtils.endsWithIgnoreCase(targetUrl, ".html")){redirectStrategy.sendRedirect(request, response,securityProperties.getBrowser().getLoginPage());}}return new SimpleResponse("請(qǐng)求的服務(wù)需要身份認(rèn)證,請(qǐng)引導(dǎo)用戶到登錄頁(yè)面");} } BrowserSecurityController.java?
?
在啟動(dòng)項(xiàng)目中的application.properties文件中配置登錄頁(yè)面:
# 配置登錄頁(yè)面 getword.security.browser.loginPage=/demo.html?
?
讀取配置文件信息:
import org.springframework.boot.context.properties.ConfigurationProperties;// 讀取前綴為getword.security的屬性配置,其中browser中的屬性會(huì)被讀取到browserProperties中 @ConfigurationProperties(prefix = "getword.security") public class SecurityProperties {// browser的屬性會(huì)匹配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"; //默認(rèn)值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());//工具類, 將對(duì)象轉(zhuǎn)成json @Autowiredprivate ObjectMapper objectMapper;// 登錄成功后調(diào)用 @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注冊(cè),使處理器生效:
?
?
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()); //服務(wù)器內(nèi)部錯(cuò)誤response.setContentType("application/json;charset=utf-8");response.getWriter().write(objectMapper.writeValueAsString(e));} } VstudyAuthenticationFailHandler.java?
配置:和success類似
?
4)判斷請(qǐng)求方式,做出相應(yīng)的處理
successHandler:
@Component("vstudyAuthenticationSuccessHandler") public class VstudyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {private Logger logger = LoggerFactory.getLogger(getClass());//工具類, 將對(duì)象轉(zhuǎn)成json @Autowiredprivate ObjectMapper objectMapper;@Autowiredprivate SecurityProperties securityProperties; //獲取配置信息// 登錄成功后調(diào)用 @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{// 調(diào)用父類方法,完成重定向跳轉(zhuǎn)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()); //服務(wù)器內(nèi)部錯(cuò)誤response.setContentType("application/json;charset=utf-8");response.getWriter().write(objectMapper.writeValueAsString(e));}else{super.onAuthenticationFailure(request, response, e);}} } VstudyAuthenticationFailHandler?
?
2 認(rèn)證流程
?
?
3 圖形驗(yàn)證碼
3.1 生成圖形驗(yàn)證碼
驗(yàn)證碼圖片信息:
public class ImageCode {private BufferedImage image;private String code;private LocalDateTime expireTime;//過(guò)期時(shí)間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驗(yàn)證碼* @param request* @return*/public ImageCode createImageCode(ServletWebRequest request){// 生成驗(yàn)證碼,方法很多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);}/*** 生成隨機(jī)背景條紋** @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 驗(yàn)證碼校驗(yàn)
?自定義過(guò)濾器:
public class ValidateCodeFilter extends OncePerRequestFilter {/*** 驗(yàn)證碼校驗(yàn)失敗處理器*/private AuthenticationFailureHandler authenticationFailureHandler;/*** 系統(tǒng)配置信息*/private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();/*** 系統(tǒng)中的校驗(yàn)碼處理器*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 只處理登錄請(qǐng)求if (StringUtils.equals("/authetication/form", request.getRequestURI())&& StringUtils.equalsIgnoreCase(request.getMethod(), "POST")) {try {logger.info("驗(yàn)證碼校驗(yàn)通過(guò)");} catch (ValidateCodeException e) {//驗(yàn)證失敗 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,當(dāng)前請(qǐng)求參數(shù)String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");if(StringUtils.isBlank(codeInRequest)){throw new ValidateCodeException("驗(yàn)證碼不能為空");}if(codeInSession==null){throw new ValidateCodeException("驗(yàn)證碼不存在");}if(codeInSession.isExpired()){throw new ValidateCodeException("驗(yàn)證碼已過(guò)期");}if(!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)){throw new ValidateCodeException("驗(yàn)證碼不匹配");}sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);}public AuthenticationFailureHandler getAuthenticationFailureHandler() {return authenticationFailureHandler;}public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {this.authenticationFailureHandler = authenticationFailureHandler;} } ValidateCodeFilter?
?
配置:
?
執(zhí)行流程:
?/index.html ->redirect ->/authentication/require(控制器,判斷是.html結(jié)尾)->login.html ->ValidateCodeFilter ->exception ->?VstudyAuthenticationFailHandler ->loginType:JSON
?
login.html中,使用ajax發(fā)送登錄請(qǐng)求 -> 驗(yàn)證碼過(guò)濾器通過(guò) -> UsernamePasswordFilter通過(guò) -> 返回登錄結(jié)果信息
?
3.3 驗(yàn)證碼的接口
- 為了方便修改驗(yàn)證碼的參數(shù),如寬度、高度、長(zhǎng)度等信息,我們將通過(guò)配置文件的形式配置這些信息。還有驗(yàn)證碼的URL地址。
- 驗(yàn)證碼攔截的接口可配置,比如為了限制用戶操作頻率,對(duì)用戶操作使用驗(yàn)證碼進(jìn)行限制。驗(yàn)證碼過(guò)濾器可以攔截多個(gè)控制器請(qǐng)求。
- 驗(yàn)證碼的生成邏輯可以配置
三級(jí)配置:
?
?1)驗(yàn)證碼參數(shù):
默認(rèn)配置:
/*** 圖形驗(yàn)證碼*/ public class ImageCodeProperties {private int width = 67;private int height = 23;private int len = 4;private int expireIn = 60; //60秒后過(guò)期 //seter getter }?
?驗(yàn)證碼信息:
/*** 驗(yàn)證碼:包括圖形驗(yàn)證碼、短信驗(yàn)證碼*/ public class ValidateCodeProperties {private ImageCodeProperties image; }?
?屬性讀取:
@ConfigurationProperties(prefix = "getword.security") public class SecurityProperties {// browser的屬性會(huì)匹配getword.security.browser后面的屬性private BrowserProperties browser = new BrowserProperties();// 驗(yàn)證碼屬性匹配getword.security.code后面的屬性private ValidateCodeProperties code = new ValidateCodeProperties(); }?
?
?
?
?
?
end
轉(zhuǎn)載于:https://www.cnblogs.com/zhuxiang1633/p/10311320.html
總結(jié)
以上是生活随笔為你收集整理的spring security认证的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python 代码转程序_精悍的Pyth
- 下一篇: VBoxGuestAdditions加载