摘要:截獲頁面錯誤處理邏輯關(guān)于權(quán)限等授權(quán)信息,可以直接放到中實(shí)現(xiàn)緩存。我認(rèn)為也是不錯的。源碼奉上分支溫馨提示平時測試代碼可能比較亂。如果更好的實(shí)現(xiàn),讓我學(xué)習(xí),讓我我進(jìn)步,請聯(lián)系我。
關(guān)于驗(yàn)證大致分為兩個方面:
用戶登錄時的驗(yàn)證;
用戶登錄后每次訪問時的權(quán)限認(rèn)證
主要解決方法:使用自定義的Shiro Filter 項(xiàng)目搭建:pom.mx引入相關(guān)jar包
org.apache.shiro shiro-spring ${shiro.version} org.apache.shiro shiro-core ${shiro.version} io.jsonwebtoken jjwt 0.9.0
Shrio 的相關(guān)配置
劃重點(diǎn)??!自定義了一個Filter
filterMap.put("JWTFilter", new JWTFilter());
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 添加自己的過濾器并且取名為JWTFilter MapfilterMap = new HashMap<>(); filterMap.put("JWTFilter", new JWTFilter()); shiroFilterFactoryBean.setFilters(filterMap); /* * 自定義url規(guī)則 * http://shiro.apache.org/web.html#urls- */ Map filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap(); filterChainDefinitionMap.put("/**", "JWTFilter"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * securityManager 不用直接注入shiroDBRealm,可能會導(dǎo)致事務(wù)失效 * 解決方法見 handleContextRefresh * http://www.debugrun.com/a/NKS9EJQ.html */ @Bean("securityManager") public DefaultWebSecurityManager securityManager(TokenRealm tokenRealm) { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(tokenRealm); /* * 關(guān)閉shiro自帶的session,詳情見文檔 * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29 */ DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); manager.setSubjectDAO(subjectDAO); return manager; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean(name = "TokenRealm") @DependsOn("lifecycleBeanPostProcessor") public TokenRealm tokenRealm() { return new TokenRealm(); } @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); // 強(qiáng)制使用cglib,防止重復(fù)代理和可能引起代理出錯的問題 // https://zhuanlan.zhihu.com/p/29161098 defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return new AuthorizationAttributeSourceAdvisor(); } }
自定義Shrio filter
執(zhí)行順序:preHandle -> doFilterInternal -> executeLogin -> onLoginSuccesspublic class JWTFilter extends BasicHttpAuthenticationFilter { /** * 自定義執(zhí)行登錄的方法 */ @Override protected boolean executeLogin(ServletRequest request, ServletResponse response) throws IOException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; UsernamePasswordToken usernamePasswordToken = JSON.parseObject(httpServletRequest.getInputStream(), UsernamePasswordToken.class); // 提交給realm進(jìn)行登入,如果錯誤他會拋出異常并被捕獲 Subject subject = this.getSubject(request, response); subject.login(usernamePasswordToken); return this.onLoginSuccess(usernamePasswordToken, subject, request, response); //錯誤拋出異常 } /** * 最先執(zhí)行的方法 */ @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { return super.preHandle(request, response); } /** * 登錄成功后登錄的操作 * 加上jwt 的header */ @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) { HttpServletResponse httpServletResponse = (HttpServletResponse) response; String jwtToken = Jwts.builder() .setId(token.getPrincipal().toString()) .setExpiration(DateTime.now().plusMinutes(30).toDate()) .signWith(SignatureAlgorithm.HS256, JWTCost.signatureKey) .compact(); httpServletResponse.addHeader(AUTHORIZATION_HEADER, jwtToken); return true; } /** * 登錄以及校驗(yàn)的主要流程 * 判斷是否是登錄,或者是登陸后普通的一次請求 */ @Override public void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; String servletPath = httpServletRequest.getServletPath(); if (StringUtils.equals(servletPath, "/login")) { //執(zhí)行登錄 this.executeLogin(servletRequest, servletResponse); } else { String authenticationHeader = httpServletRequest.getHeader(AUTHORIZATION_HEADER); if (StringUtils.isNotEmpty(authenticationHeader)) { Claims body = Jwts.parser() .setSigningKey(JWTCost.signatureKey) .parseClaimsJws(authenticationHeader) .getBody(); if (body != null) { //更新token body.setExpiration(DateTime.now().plusMinutes(30).toDate()); String updateToken = Jwts.builder().setClaims(body).compact(); httpServletResponse.addHeader(AUTHORIZATION_HEADER, updateToken); //添加用戶憑證 PrincipalCollection principals = new SimplePrincipalCollection(body.getId(), JWTCost.UserNamePasswordRealm);//拼裝shiro用戶信息 WebSubject.Builder builder = new WebSubject.Builder(servletRequest, servletResponse); builder.principals(principals); builder.authenticated(true); builder.sessionCreationEnabled(false); WebSubject subject = builder.buildWebSubject(); //塞入容器,統(tǒng)一調(diào)用 ThreadContext.bind(subject); filterChain.doFilter(httpServletRequest, httpServletResponse); } } else { httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value()); } } } }
登錄失敗處理
處理Shrio異常@RestControllerAdvice public class GlobalControllerExceptionHandler { @ExceptionHandler(value = Exception.class) public Object allExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { String message = exception.getCause().getMessage(); LogUtil.error(message); return new ResultInfo(exception.getClass().getName(), message); } /*=========== Shiro 異常攔截==============*/ @ExceptionHandler(value = IncorrectCredentialsException.class) public String IncorrectCredentialsException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "IncorrectCredentialsException"; } @ExceptionHandler(value = UnknownAccountException.class) public String UnknownAccountException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "UnknownAccountException"; } @ExceptionHandler(value = LockedAccountException.class) public String LockedAccountException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "LockedAccountException"; } @ExceptionHandler(value = ExcessiveAttemptsException.class) public String ExcessiveAttemptsException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "ExcessiveAttemptsException"; } @ExceptionHandler(value = AuthenticationException.class) public String AuthenticationException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "AuthenticationException"; } @ExceptionHandler(value = UnauthorizedException.class) public String UnauthorizedException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "UnauthorizedException"; } }處理JWT異常 這是個坑,因?yàn)槭窃?b>filter內(nèi)發(fā)生的異常,@ExceptionHandler是截獲不到的。
/** * 截獲spring boot Error頁面 */ @RestController public class GlobalExceptionHandler implements ErrorController { @Override public String getErrorPath() { return "/error"; } @RequestMapping(value = "/error") public Object error(HttpServletRequest request, HttpServletResponse response) throws Exception { // 錯誤處理邏輯 Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception"); Throwable cause = exception.getCause(); if (cause instanceof ExpiredJwtException) { response.setStatus(HttpStatus.GATEWAY_TIMEOUT.value()); return new ResultInfo("ExpiredJwtException", cause.getMessage()); } if (cause instanceof MalformedJwtException) { response.setStatus(HttpStatus.FORBIDDEN.value()); return new ResultInfo("MalformedJwtException", cause.getMessage()); } return new ResultInfo(cause.getCause().getMessage(), cause.getMessage()); } }關(guān)于權(quán)限等授權(quán)信息,可以直接放到Redis中實(shí)現(xiàn)緩存。我認(rèn)為也是不錯的。
源碼奉上:githup-shiro分支 :溫馨提示:平時測試代碼可能比較亂。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69300.html
摘要:創(chuàng)建應(yīng)用有很多方法去創(chuàng)建項(xiàng)目,官方也推薦用在線項(xiàng)目創(chuàng)建工具可以方便選擇你要用的組件,命令行工具當(dāng)然也可以。對于開發(fā)人員最大的好處在于可以對應(yīng)用進(jìn)行自動配置。 使用JWT保護(hù)你的Spring Boot應(yīng)用 - Spring Security實(shí)戰(zhàn) 作者 freewolf 原創(chuàng)文章轉(zhuǎn)載請標(biāo)明出處 關(guān)鍵詞 Spring Boot、OAuth 2.0、JWT、Spring Security、SS...
摘要:配置中心在軟件開發(fā)中隨著業(yè)務(wù)的需要需求的變更程序的靈活我們時常需要在項(xiàng)目中設(shè)置各種開關(guān)或者配置項(xiàng)在往常時一般會采用配置文件的方式但是在這分布式集群時代采用傳統(tǒng)的配置管理方式顯得有點(diǎn)力不從心同時在我們的終端我們也時常需要各種配置在面對大量的終 ______ _ ______ |_ _ `. (_) . ___ | | | `...
摘要:配置中心在軟件開發(fā)中隨著業(yè)務(wù)的需要需求的變更程序的靈活我們時常需要在項(xiàng)目中設(shè)置各種開關(guān)或者配置項(xiàng)在往常時一般會采用配置文件的方式但是在這分布式集群時代采用傳統(tǒng)的配置管理方式顯得有點(diǎn)力不從心同時在我們的終端我們也時常需要各種配置在面對大量的終 ______ _ ______ |_ _ `. (_) . ___ | | | `...
摘要:這里使用的是數(shù)據(jù)庫啟動類上加上注解在啟動類中添加對包掃描掃描多個包下的可以有以下幾種方法掃描會自動加載相關(guān)配置,數(shù)據(jù)源就會自動注入到中,會自動注入到中,可以直接使用。有配置文件下的使用掃描多個包下的可以有以下幾種方法掃描 Spring-Boot 學(xué)習(xí)筆記 1 Spring-Boot 介紹 1.1 什么是Spring-Boot Spring-Boot是由Pivotal團(tuán)隊提供的全新框架...
閱讀 3095·2021-11-22 13:54
閱讀 844·2021-11-04 16:08
閱讀 4554·2021-10-11 11:09
閱讀 3609·2021-09-22 16:05
閱讀 945·2019-08-30 15:54
閱讀 403·2019-08-30 15:44
閱讀 607·2019-08-30 14:05
閱讀 1027·2019-08-30 12:46