Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
Spring Security 是能夠?yàn)镾pring 企業(yè)應(yīng)用提供聲明式的安全訪問(wèn)控制解決方案的安全框架,為應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制功能,減少為企業(yè)系統(tǒng)安全訪問(wèn)控制編寫大量重復(fù)的代碼。
spring-security
├── 核心 - spring-security-core.jar
├── Remoting - spring-security-remoting.jar
├── Web - spring-security-web.jar
├── 配置 - spring-security-config.jar
├── LDAP - spring-security-ldap.jar
├── OAuth 2.0核心 - spring-security-oauth2-core.jar
├── OAuth 2.0客戶端 - spring-security-oauth2-client.jar
├── OAuth 2.0 JOSE - spring-security-oauth2-jose.jar
├── ACL - spring-security-acl.jar
├── CAS - spring-security-cas.jar
├── OpenID - spring-security-openid.jar
└── 測(cè)試 - spring-security-test.jar
核心詳細(xì)描述參考中文官網(wǎng)
Spring Security 支持Maven和Gradle集成,本文主要使用Spring Boot與Maven: pom.xml
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
代碼清單:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/user/**").hasRole("USER")
)
.formLogin((formLogin) -> formLogin
.loginPage("/login")
.failureUrl("/login-error")
);
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails=User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
}
當(dāng)添加了SecurityConfig 之后我們的應(yīng)用就具備如下功能:
從類關(guān)系圖可以清楚**@EnableWebSecurity**注解是開(kāi)啟Security安全功能的核心注解,EnableWebSecurity源碼清單:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}
@EnableWebSecurity是組合注解,引入了Impost注解包含的外部配置以及激活了@EnableGlobalAuthentication注解,而@EnableGlobalAuthentication注解激活了AuthenticationConfiguration認(rèn)證配置類。
WebSecurityConfiguration 是web安全配置核心類,WebSecurityConfiguration最主要的功能就是創(chuàng)建了springSecurityFilterChain Bean,springSecurityFilterChain 是spring security的核心過(guò)濾器,是整個(gè)認(rèn)證的入口。WebSecurityConfiguration中完成了聲明springSecurityFilterChain的作用,并且最終交給DelegatingFilterProxy這個(gè)代理類,負(fù)責(zé)攔截請(qǐng)求(注意DelegatingFilterProxy這個(gè)類不是spring security包中的,而是存在于web包中,spring使用了代理模式來(lái)實(shí)現(xiàn)安全過(guò)濾的解耦)。WebSecurityConfiguration源碼清單:
@Configuration(proxyBeanMethods=false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
//省略==========================================@Bean(name=AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers=this.webSecurityConfigurers !=null && !this.webSecurityConfigurers.isEmpty();
boolean hasFilterChain=!this.securityFilterChains.isEmpty();
Assert.state(!(hasConfigurers && hasFilterChain),
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter=this.objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
customizer.customize(this.webSecurity);
}
return this.webSecurity.build();
}
//省略=====================}
SpringWebMvcImportSelector主要作用是判斷當(dāng)前的環(huán)境是否包含springmvc,因?yàn)閟pring security可以在非spring環(huán)境下使用,為了避免DispatcherServlet的重復(fù)配置,所以使用了這個(gè)注解來(lái)區(qū)分。
OAuth2ImportSelector類是為了對(duì) OAuth2.0 開(kāi)放授權(quán)協(xié)議進(jìn)行支持。ClientRegistration 如果被引用,具體點(diǎn)也就是 spring-security-oauth2 模塊被啟用(引入依賴jar)時(shí)。會(huì)啟用 OAuth2 客戶端配置 OAuth2ClientConfiguration
HttpSecurityConfiguration配置類首先通過(guò)@Autowired去獲取容器中的一個(gè)AuthenticationManager實(shí)例,如果沒(méi)能獲取到則使用依賴注入的AuthenticationConfiguration實(shí)例創(chuàng)建一個(gè)AuthenticationManager實(shí)例,這個(gè)實(shí)例其實(shí)就是ProviderManager。然后初始化httpSecurity。
通過(guò)HttpSecurity配置指明了Web Security 攔截什么URL、登錄認(rèn)證方式、設(shè)置什么權(quán)限等。
AuthenticationConfiguration 主要作用就是創(chuàng)建全局的身份認(rèn)證管理者AuthenticationManager,AuthenticationManager便是最核心的身份認(rèn)證管理器。 AuthenticationConfiguration源碼清單:
@Configuration(proxyBeanMethods=false)
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
//省略================@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
ApplicationContext context) {
LazyPasswordEncoder defaultPasswordEncoder=new LazyPasswordEncoder(context);
AuthenticationEventPublisher authenticationEventPublisher=getBeanOrNull(context,
AuthenticationEventPublisher.class);
DefaultPasswordEncoderAuthenticationManagerBuilder result=new DefaultPasswordEncoderAuthenticationManagerBuilder(
objectPostProcessor, defaultPasswordEncoder);
if (authenticationEventPublisher !=null) {
result.authenticationEventPublisher(authenticationEventPublisher);
}
return result;
}
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
ApplicationContext context) {
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(
ApplicationContext context) {
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
public AuthenticationManager getAuthenticationManager() throws Exception {
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
AuthenticationManagerBuilder authBuilder=this.applicationContext.getBean(AuthenticationManagerBuilder.class);
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
authBuilder.apply(config);
}
this.authenticationManager=authBuilder.build();
if (this.authenticationManager==null) {
this.authenticationManager=getAuthenticationManagerBean();
}
this.authenticationManagerInitialized=true;
return this.authenticationManager;
}
//省略================private AuthenticationManager getAuthenticationManagerBean() {
return lazyBean(AuthenticationManager.class);
}
AuthenticationManager 提供了認(rèn)證的入口,源碼清單:
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
AuthenticationManager 接收 Authentication 對(duì)象作為參數(shù),并通過(guò) authenticate(Authentication) 方法對(duì)其進(jìn)行驗(yàn)證;AuthenticationProvider實(shí)現(xiàn)類用來(lái)支撐對(duì) Authentication 對(duì)象的驗(yàn)證動(dòng)作;UsernamePasswordAuthenticationToken實(shí)現(xiàn)了 Authentication主要是將用戶輸入的用戶名和密碼進(jìn)行封裝,并供給 AuthenticationManager 進(jìn)行驗(yàn)證;驗(yàn)證完成以后將返回一個(gè)認(rèn)證成功的 Authentication 對(duì)象;
Authentication 源碼:
public interface Authentication extends Principal, Serializable {
//#1.權(quán)限集合
Collection<? extends GrantedAuthority> getAuthorities();
//#2. 用戶密碼認(rèn)證時(shí),可以理解為密碼
Object getCredentials();
//#3. 認(rèn)證時(shí)包含的信息
Object getDetails();
//# 4. 用戶密碼認(rèn)證時(shí),可以理解為用戶名
Object getPrincipal();
//# 5. 是否被認(rèn)證,認(rèn)證為true
boolean isAuthenticated();
//# 6. 設(shè)置是否能夠被認(rèn)證
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
ProviderManager 它是 AuthenticationManager 的一個(gè)實(shí)現(xiàn)類,提供了基本的認(rèn)證邏輯和方法;它包含了一個(gè) List<AuthenticationProvider> 對(duì)象,通過(guò) AuthenticationProvider 接口來(lái)擴(kuò)展出不同的認(rèn)證提供者(當(dāng)Spring Security默認(rèn)提供的實(shí)現(xiàn)類不能滿足需求的時(shí)候可以擴(kuò)展AuthenticationProvider 覆蓋supports(Class<?> authentication) 方法);
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//# 獲取當(dāng)前認(rèn)證類型
Class<? extends Authentication> toTest=authentication.getClass();
AuthenticationException lastException=null;
AuthenticationException parentException=null;
Authentication result=null;
Authentication parentResult=null;
int currentPosition=0;
int size=this.providers.size();
//遍歷所有providers,調(diào)用supports驗(yàn)證是否支持當(dāng)前認(rèn)證
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
provider.getClass().getSimpleName(), ++currentPosition, size));
}
try {
//調(diào)用provider的認(rèn)證方法進(jìn)行認(rèn)證
result=provider.authenticate(authentication);
if (result !=null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException ex) {
prepareException(ex, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw ex;
}
catch (AuthenticationException ex) {
lastException=ex;
}
}
if (result==null && this.parent !=null) {
// Allow the parent to try.
try {
//認(rèn)證失敗,調(diào)用父類的認(rèn)證方法進(jìn)行認(rèn)證
parentResult=this.parent.authenticate(authentication);
result=parentResult;
}
catch (ProviderNotFoundException ex) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException ex) {
parentException=ex;
lastException=ex;
}
}
if (result !=null) {
if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful then it
// will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent
// AuthenticationManager already published it
if (parentResult==null) {
this.eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException==null) {
lastException=new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",
new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));
}
// If the parent AuthenticationManager was attempted and failed then it will
// publish an AbstractAuthenticationFailureEvent
// This check prevents a duplicate AbstractAuthenticationFailureEvent if the
// parent AuthenticationManager already published it
if (parentException==null) {
prepareException(lastException, authentication);
}
throw lastException;
}
AuthenticationProvider, ProviderManager通過(guò)AuthenticationProvider擴(kuò)展多種認(rèn)證方法,AuthenticationProvider 本身也就是一個(gè)接口,從類圖中我們可以看出它的實(shí)現(xiàn)類AbstractUserDetailsAuthenticationProvider 和AbstractUserDetailsAuthenticationProvider的子類DaoAuthenticationProvider 。
DaoAuthenticationProvider 是Spring Security中一個(gè)核心的Provider,對(duì)所有的數(shù)據(jù)庫(kù)提供了基本方法和入口。DaoAuthenticationProvider源碼清單:
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
/**
* 實(shí)現(xiàn)用戶密碼加密
*/
private PasswordEncoder passwordEncoder;
//省略
/**
*
* 實(shí)現(xiàn) additionalAuthenticationChecks 的驗(yàn)證方法(主要驗(yàn)證密碼);
*/
@Override
@SuppressWarnings("deprecation")
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials()==null) {
this.logger.debug("Failed to authenticate since no credentials provided");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
String presentedPassword=authentication.getCredentials().toString();
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Failed to authenticate since password does not match stored value");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
//省略
/**
* 實(shí)現(xiàn)了 AbstractUserDetailsAuthenticationProvider retrieveUser 抽象方法
* 主要是通過(guò)注入U(xiǎn)serDetailsService接口對(duì)象,并調(diào)用其接口方法 loadUserByUsername(String username)獲取得到相關(guān)的用戶信息。
* UserDetailsService接口非常重要。
*/
@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
//數(shù)據(jù)庫(kù)獲取用戶信息的擴(kuò)展點(diǎn)
UserDetails loadedUser=this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser==null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
//省略
}
AbstractUserDetailsAuthenticationProvider主要實(shí)現(xiàn)了AuthenticationProvider的接口方法 authenticate 并提供了相關(guān)的驗(yàn)證邏輯;
抽象獲取用戶信息方法
protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException;
三步驗(yàn)證工作: i. preAuthenticationChecks ii. additionalAuthenticationChecks(抽象方法,子類實(shí)現(xiàn)) iii. postAuthenticationChecks將驗(yàn)證結(jié)果封裝成UsernamePasswordAuthenticationToken返回。
源碼清單:
public abstract class AbstractUserDetailsAuthenticationProvider
implements AuthenticationProvider, InitializingBean, MessageSourceAware {
//省略
//抽象驗(yàn)證方法(主要驗(yàn)證密碼)
protected abstract void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;
//省略
/**
*
* 實(shí)現(xiàn)AuthenticationProvider.authenticate驗(yàn)證方法
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
String username=determineUsername(authentication);
boolean cacheWasUsed=true;
UserDetails user=this.userCache.getUserFromCache(username);
if (user==null) {
cacheWasUsed=false;
try {
//1. 獲取用戶信息
user=retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
}
catch (UsernameNotFoundException ex) {
this.logger.debug("Failed to find user '" + username + "'");
if (!this.hideUserNotFoundExceptions) {
throw ex;
}
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
}
try {
//2. 預(yù)檢查由DefaultPreAuthenticationChecks類實(shí)現(xiàn)(主要判斷當(dāng)前用戶是否鎖定,過(guò)期,凍結(jié)User接口)
this.preAuthenticationChecks.check(user);
//3. 子類實(shí)現(xiàn)
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException ex) {
if (!cacheWasUsed) {
throw ex;
}
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed=false;
user=retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
this.preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
//#4.檢測(cè)用戶密碼是否過(guò)期對(duì)應(yīng)#2 的User接口
this.postAuthenticationChecks.check(user);
if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn=user;
if (this.forcePrincipalAsString) {
principalToReturn=user.getUsername();
}
//4. 將驗(yàn)證結(jié)果封裝成UsernamePasswordAuthenticationToken
return createSuccessAuthentication(principalToReturn, authentication, user);
}
private String determineUsername(Authentication authentication) {
return (authentication.getPrincipal()==null) ? "NONE_PROVIDED" : authentication.getName();
}
//封裝驗(yàn)證結(jié)果
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
UserDetails user) {
// Ensure we return the original credentials the user supplied,
// so subsequent attempts are successful even with encoded passwords.
// Also ensure we return the original getDetails(), so that future
// authentication events after cache expiry contain the details
UsernamePasswordAuthenticationToken result=new UsernamePasswordAuthenticationToken(principal,
authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
this.logger.debug("Authenticated user");
return result;
}
//抽象獲取用戶信息接口
protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException;
//省略
}
UserDetailsService接口作為橋梁,是DaoAuthenticationProvier與特定用戶信息來(lái)源進(jìn)行解耦的地方,UserDetailsService由UserDetails和UserDetailsManager所構(gòu)成;UserDetails和UserDetailsManager各司其責(zé),一個(gè)是對(duì)基本用戶信息進(jìn)行封裝,一個(gè)是對(duì)基本用戶信息進(jìn)行管理;特別注意,UserDetailsService、UserDetails以及UserDetailsManager都是可被用戶自定義的擴(kuò)展點(diǎn),我們可以繼承這些接口提供自己的讀取用戶來(lái)源和管理用戶的方法,比如我們可以自己實(shí)現(xiàn)一個(gè) 與特定 ORM 框架,比如 Mybatis 或者 Hibernate,相關(guān)的UserDetailsService和UserDetailsManager;
UserDetails 驗(yàn)證用戶實(shí)體
public interface UserDetails extends Serializable {
#1.權(quán)限集合
Collection<? extends GrantedAuthority> getAuthorities();
#2.密碼
String getPassword();
#3.用戶民
String getUsername();
#4.用戶是否過(guò)期
boolean isAccountNonExpired();
#5.是否鎖定
boolean isAccountNonLocked();
#6.用戶密碼是否過(guò)期
boolean isCredentialsNonExpired();
#7.賬號(hào)是否可用(可理解為是否刪除)
boolean isEnabled();
}
文由掘金@天行天忌授權(quán)發(fā)布,前端晚間課對(duì)其內(nèi)容進(jìn)行微改。
HTML,超文本標(biāo)記語(yǔ)言,是一種用于創(chuàng)建網(wǎng)頁(yè)的標(biāo)準(zhǔn)標(biāo)記語(yǔ)言。自從引入 HTML 以來(lái),它就一直用于構(gòu)建互聯(lián)網(wǎng)。與 JavaScript 和 CSS 一起,HTML 構(gòu)成前端開(kāi)發(fā)的三劍客。
盡管許多新技術(shù)使網(wǎng)站創(chuàng)建過(guò)程變得更簡(jiǎn)單、更高效,但 HTML 始終是核心。隨著 HTML5 的普及,在 2014 年,這種標(biāo)記語(yǔ)言發(fā)生了很多變化,變得更加友好,瀏覽器對(duì)新標(biāo)準(zhǔn)的支持熱度也越來(lái)越高。而HTML并不止于此,還在不斷發(fā)生變化,并且可能會(huì)獲得一些特性來(lái)證明對(duì) HTML6 的命名更改是合理的。
該元素<dialog> 將隨 HTML6 一起提供。它被認(rèn)為等同于用 JavaScript 開(kāi)發(fā)的模態(tài),并且已經(jīng)標(biāo)準(zhǔn)化,但只有少數(shù)瀏覽器完全支持。但這種現(xiàn)象會(huì)改變,很快它將在所有瀏覽器中得到支持。
這個(gè)元素在其默認(rèn)格式下,只會(huì)將光標(biāo)顯示在它所在的位置上,但可以使用 JavaScript 打開(kāi)模式。
<dialog>
<form method="dialog">
<input type="submit" value="確定" />
<input type="submit" value="取消" />
</form>
</dialog>
在默認(rèn)形式下,該元素創(chuàng)建一個(gè)灰色背景,其下方是非交互式內(nèi)容。
可以在 <dialog> 其中的表單上使用一種方法,該方法將發(fā)送值并將其傳遞回自身 <dialog>。
總的來(lái)說(shuō),這個(gè)標(biāo)簽在用戶交互和改進(jìn)的界面中變得有益。
可以通過(guò)更改 <dialog> 標(biāo)簽的 open 屬性以控制打開(kāi)和關(guān)閉。
<dialog open>
<p>組件內(nèi)容</p>
</dialog>
FutureClaw 雜志主編 Bobby Mozumder 建議:
將錨元素鏈接到 JSON/XML、API 端點(diǎn),讓瀏覽器在內(nèi)部將數(shù)據(jù)加載到新的數(shù)據(jù)結(jié)構(gòu)中,然后瀏覽器將 DOM 元素替換為根據(jù)需要加載的任何數(shù)據(jù)。初始數(shù)據(jù)(以及標(biāo)準(zhǔn)錯(cuò)誤響應(yīng))可以放在標(biāo)題裝置中,如果需要,可以稍后替換。
據(jù)他介紹,這是單頁(yè)應(yīng)用程序網(wǎng)頁(yè)設(shè)計(jì)模式,可以提高響應(yīng)速度和加載時(shí)間,因?yàn)椴恍枰虞d JavaScript。
這個(gè)是一個(gè)比較有意思的提案,就有點(diǎn)類似我們以前沒(méi)有做前后端分離之前的混合編程的模式,HTML變成模板語(yǔ)言,通過(guò)JSON API請(qǐng)求數(shù)據(jù),不一樣的是變成瀏覽器來(lái)默認(rèn)解析,瀏覽器內(nèi)部加載數(shù)據(jù)到新的數(shù)據(jù)結(jié)構(gòu)中,然后瀏覽器將按需加載到的數(shù)據(jù)替換成 DOM 元素。
大家可以看一下InfoQ上的這篇文章《針對(duì)非正式 HTML6 提案“無(wú)需 JavaScript 的單頁(yè)應(yīng)用”引發(fā)的論戰(zhàn)》,了解更多!
https://www.infoq.cn/article/2015/03/html6-without-javascript
HTML6 愛(ài)好者相信即將到來(lái)的更新將允許瀏覽器調(diào)整圖像大小以獲得更好的觀看體驗(yàn)。
每個(gè)瀏覽器都難以呈現(xiàn)相對(duì)于設(shè)備和屏幕尺寸的最佳圖像尺寸,不幸的是,src 標(biāo)簽 img 在處理這個(gè)問(wèn)題時(shí)不是很有效。
這個(gè)問(wèn)題可以通過(guò)一個(gè)新標(biāo)簽 <srcset> 來(lái)解決,它使瀏覽器在多個(gè)圖像之間進(jìn)行選擇的工作變得更加容易。
將可用庫(kù)引入 HTML6 絕對(duì)是提高開(kāi)發(fā)效率的重要一步。
很多時(shí)候,需要在互聯(lián)網(wǎng)上定義一般信息,而這些一般信息可以是任何公開(kāi)的信息,例如電話號(hào)碼、姓名、地址等。微格式是能夠定義一般數(shù)據(jù)的標(biāo)準(zhǔn)。微格式可以增強(qiáng)設(shè)計(jì)者的能力,并可以減少搜索引擎推斷公共信息所需的努力。
盡管標(biāo)簽<ul>、<ol>非常有用,但在某些情況下仍有一些不足之處。可以處理交互元素的標(biāo)簽將是一個(gè)不錯(cuò)的選擇。
這就是創(chuàng)建標(biāo)簽 <menu> 的驅(qū)動(dòng)力,它可以處理按鈕驅(qū)動(dòng)的列表元素。
<menu type="toolbar">
<li><button>個(gè)人信息</button></li>
<li><button>系統(tǒng)設(shè)置</button></li>
<li><button>賬號(hào)注銷</button></li>
</menu>
因此 <menu>,除了能夠像普通列表一樣運(yùn)行之外,還可以增強(qiáng) HTML 列表的功能。
雖然HTML5在安全性方面還不錯(cuò),瀏覽器和網(wǎng)絡(luò)技術(shù)也提供了合理的保護(hù)。毫無(wú)疑問(wèn),在身份驗(yàn)證和安全領(lǐng)域還有很多事情可以做。如密鑰可以異地存儲(chǔ);這將防止不受歡迎的人訪問(wèn)并支持身份驗(yàn)證。使用嵌入式密鑰而不是 cookie,使數(shù)字簽名更好等。
HTML6 允許以更好的方式使用設(shè)備上的相機(jī)和媒體。將能夠控制相機(jī)、它的效果、模式、全景圖像、HDR 和其他屬性。
沒(méi)有什么是完美的,HTML 也不是完美的,所以 HTML 規(guī)范可以做很多事情來(lái)使它更好。應(yīng)該對(duì)一些有用的規(guī)范進(jìn)行標(biāo)準(zhǔn)化,以增強(qiáng) HTML 的能力。小的變化已經(jīng)開(kāi)始推出。如增強(qiáng)藍(lán)牙支持、p2p 文件傳輸、惡意軟件保護(hù)、云存儲(chǔ)集成,下一個(gè) HTML 版本可以考慮一下。
解決企業(yè)“注銷難”的問(wèn)題,市市場(chǎng)監(jiān)管局說(shuō):今天,上海市注銷企業(yè)“一窗通”網(wǎng)上平臺(tái)正式上線→企業(yè)填報(bào)注銷申請(qǐng)信息后,平臺(tái)同步推送給市場(chǎng)監(jiān)管、稅務(wù)、人社、商務(wù)、海關(guān)、人民銀行、外管等行政部門以及人民法院等司法部門,各部門并聯(lián)預(yù)審辦理,提高效率。平臺(tái)還提供銀行賬戶撤銷的預(yù)約服務(wù)功能和在國(guó)家企業(yè)信用信息公示系統(tǒng)免費(fèi)發(fā)布注銷公告的功能。怎么使用?看過(guò)來(lái)↓
企業(yè)注銷流程再造
企業(yè)填報(bào)注銷申請(qǐng)信息后,平臺(tái)同步推送給各相關(guān)部門,各部門并聯(lián)預(yù)審辦理。平臺(tái)引導(dǎo)企業(yè)按照最優(yōu)方案辦理注銷,幫助申請(qǐng)人了解各項(xiàng)業(yè)務(wù)的前后辦理關(guān)系。平臺(tái)還提供銀行賬戶撤銷的預(yù)約服務(wù)功能,幫助企業(yè)縮短注銷銀行賬戶的辦理時(shí)間。
注銷企業(yè)“一窗通”網(wǎng)上平臺(tái)現(xiàn)有2個(gè)入口:
第一個(gè)入口:上海市人民政府官網(wǎng)“中國(guó)上海”網(wǎng)站
(http://www.shanghai.gov.cn/)首頁(yè)上點(diǎn)擊“一網(wǎng)通辦”
然后可以在第二個(gè)頁(yè)面的“優(yōu)化營(yíng)商環(huán)境”欄目下找到“企業(yè)注銷”按鈕
第二個(gè)入口:上海市市場(chǎng)監(jiān)管局官網(wǎng)上的專欄通道
(http://www.sgs.gov.cn/shaic/index_new.html)
企業(yè)注銷規(guī)范透明
平臺(tái)為注銷申請(qǐng)人集中提供各部門辦理指南、結(jié)果反饋、全流程進(jìn)度跟蹤等電子政務(wù)服務(wù),及時(shí)告知企業(yè)辦理流程、辦理要求和進(jìn)度情況,督促各業(yè)務(wù)部門按照規(guī)定時(shí)限和要求完成注銷業(yè)務(wù)程序,進(jìn)一步實(shí)現(xiàn)注銷流程的規(guī)范化、法治化。
企業(yè)注銷便捷高效
通過(guò)平臺(tái)的信息互通共享,各部門可從平臺(tái)獲取企業(yè)信息和部門辦理結(jié)果,從而簡(jiǎn)化數(shù)據(jù)填報(bào)采集和材料提交要求,優(yōu)化辦理流程。
海關(guān)、人力資源社會(huì)保障部門可以利用注銷平臺(tái),通過(guò)數(shù)據(jù)校驗(yàn)和信息共享實(shí)現(xiàn)網(wǎng)上辦理;市場(chǎng)監(jiān)管部門可以實(shí)現(xiàn)各類申請(qǐng)文書材料的自動(dòng)生成,進(jìn)一步壓縮注銷登記辦理時(shí)間;稅務(wù)等部門可以實(shí)現(xiàn)預(yù)審并出具個(gè)性化“診斷書”,告知企業(yè)是否符合稅務(wù)免辦或即辦條件,減少和簡(jiǎn)化材料提交的數(shù)量和要求。
此外,平臺(tái)還為企業(yè)提供了在國(guó)家企業(yè)信用信息公示系統(tǒng)上免費(fèi)發(fā)布注銷公告的功能。
企業(yè)注銷智能增效
平臺(tái)通過(guò)和多個(gè)企業(yè)數(shù)據(jù)庫(kù)的對(duì)接,實(shí)現(xiàn)智能比對(duì)篩選,對(duì)企業(yè)注銷申請(qǐng)進(jìn)行分類處置,進(jìn)一步提升辦理效率:
符合簡(jiǎn)易注銷登記條件的企業(yè),可進(jìn)入國(guó)家企業(yè)信用信息公示系統(tǒng)辦理簡(jiǎn)易注銷,并可在平臺(tái)上查看簡(jiǎn)易注銷辦理進(jìn)度。
對(duì)其他企業(yè)提出的注銷申請(qǐng),平臺(tái)將相關(guān)申請(qǐng)信息同步發(fā)送給各相關(guān)業(yè)務(wù)部門,各部門進(jìn)行預(yù)審,按照各自要求對(duì)注銷企業(yè)實(shí)行分類處理。對(duì)于可通過(guò)數(shù)據(jù)共享即時(shí)辦結(jié)的,業(yè)務(wù)系統(tǒng)實(shí)時(shí)反饋平臺(tái);對(duì)于不適用線上直接辦理的,根據(jù)企業(yè)情況出具“診斷書”,將辦理本部門注銷所需提交的材料、辦理要求、聯(lián)系方式等信息通過(guò)平臺(tái)和短信反饋給企業(yè)。通過(guò)清單式告知方式,實(shí)現(xiàn)窗口辦事“一口清”。
資料:市市場(chǎng)監(jiān)管局
編輯:景雯
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。