spring-boot-learning-spring Security
SpringSecurity的简单原理:
一旦启用了Spring Security,Spring IoC容器就会为你创建一个名称为springSecurityFilterChain 的Spring Bean。
它的类型为FilterChainProxy,事实上它也实现了Filter接口, 只是它是一个特殊的拦截器。在Spring Security 操作的
过程中它会提供Servlet 过滤器DelegatingFilterProxy , 这个过滤器会通过Spring Web IoC 容器去获取
Spring Security所自动创建的FilterChainProxy 对象,这个对象上存在一个拦截器列表( List ),列表上存在用户验证
的拦截器、跨站点请求伪造等拦截器,这样它就可以提供多种拦截功能。于是焦点又落到了FilterChainProxy 对象上,通过它
还可以注册Filter ,也就是允许注册自定义的Filter 来实现对应的拦截逻辑,以满足不同的需要。当然,Spring Security
也实现了大部分常用的安全功能,并提供了相应的机制来简化开发者的工作,所以大部分情况下并不需要自定义开发,使用它提供
的机制即可
怎么开启spring security
web工程可以使用@EnableWebSecurity来驱动spring security启动。
非web工程,使用@EnableGlobalAuthentication
实际上,@EnableWebSecurity这个注解上面已经标注了@EnableGlobalAuthentication注解
WebSecurityConfigurerAdapter中的方法:
/** * WebSecurityConfigurerAdapter虽然实现了接口WebSecurityConfigurer * 但是这个实现是空实现来的,不存在任何配置 * AuthenticationManagerBuilder定义用户,密码,角色。默认不会创建任何用户和 * 密码==有登录页面没有登录用户 */ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> { /** *配置用户签名服务,user-details机制,可以给用户赋予角色 * * @param auth 签名管理构造器,用于构建用户权限控制 * @throws Exception */ protected void configure(AuthenticationManagerBuilder auth) throws Exception { this.disableLocalConfigureAuthenticationBldr = true; } /** * 用于配置Filter链 * @param web * @throws Exception */ public void configure(WebSecurity web) throws Exception { } /** * 用于配置拦截保护请求,比如什么请求放行,什么请求验证 * HttpSecurity参数方法:指定用户和角色对对应URL的访问权限。 * @param http * @throws Exception */ protected void configure(HttpSecurity http) throws Exception { this.logger.debug("Using default configure(HttpSecurity). " + "If subclassed this will potentially override subclass configure(HttpSecurity)."); ((HttpSecurity)((HttpSecurity)((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl) http.authorizeRequests().anyRequest()).authenticated().and()) .formLogin().and()).httpBasic(); }
使用内存签名服务
package com.quan.security; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; import org.springframework.security.config.annotation.web.WebSecurityConfigurer; 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.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * springSecurit默认没有任何用户配置,springboot中,如果没有,自动生成一个 * 名称为user,密码通过随机生成,可以在日志中知道。 * 自定义用户签名服务=密码:内存签名服务,数据库签名服务,自定义签名服务 */ @SpringBootApplication //@EnableWebSecurity public class SpringbootsecurityApplication extends WebSecurityConfigurerAdapter { /** * spring5后都要求使用密码编码器。 * 11建立密码编码器实例BCryptPasswordEncoder,实现接口PasswordEncoder * 是单向不可逆的密码加密方式。 * 22inMemoryAuthentication(),这个方法将返回内存保存用户信息的管理配置InMemoryUserDetailsManagerConfigurer * 启用内存缓存机制保存用户信息 * 33passwordEncoder,设置密码编码器 * 44withUser 返回UserDetailsBuilder用户详情构造器对象 * 用于配置用户信息 * 55password,设置密码,密码设置必须是经过密码编码器加密后的字符串,使用密码登录的时候是用加密前 * 66赋予角色类型。之后通过角色赋予权限 * 备注:role方法是对authorities方法的简写,role给的角色名称实际上还会加上ROLE_,如下面的源码展示 * 77and方法表示连接,开启另一个用户的注册。 * * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String p1 = passwordEncoder.encode("hllhll"); String p2 = passwordEncoder.encode("HLLHLL"); //这里是更便捷的方式 // InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> userConfig = auth.inMemoryAuthentication().passwordEncoder(passwordEncoder); // //因为使用了authorities,所以必须加入前缀 // userConfig.withUser("quanadmin") // .password(p1).authorities("ROLE_USER","ROLE_ADMIN"); // // userConfig.withUser("quanuser") // .password(p2).authorities("ROLE_USER"); auth.inMemoryAuthentication() .passwordEncoder(passwordEncoder) .withUser("quanadmin") .password(p1) .roles("USER","ADMIN") .and() .withUser("quanuser") .password(p2) .roles("USER"); } /**InMenoryUserDetailsManagerConfigurer方法 * public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication() throws Exception { * return (InMemoryUserDetailsManagerConfigurer)this.apply(new InMemoryUserDetailsManagerConfigurer()); * } * */ /**role方法 * public User.UserBuilder roles(String... roles) { * List<GrantedAuthority> authorities = new ArrayList(roles.length); * String[] var3 = roles; * int var4 = roles.length; * * for(int var5 = 0; var5 < var4; ++var5) { * String role = var3[var5]; * Assert.isTrue(!role.startsWith("ROLE_"), () -> { * return role + " cannot start with ROLE_ (it is automatically added)"; * }); * authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); * } * * return this.authorities((Collection)authorities); * } * @param */ public static void main(String[] args) { SpringApplication.run(SpringbootsecurityApplication.class, args); } }
使用数据库定义用户认证
@SpringBootApplication //@EnableWebSecurity public class SpringbootsecurityApplication extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource = null; String pwdQ = "select user_name,pwd,available from t_user where user_name = ?"; String roleQ = "select u.user_name,r.role_name from t_user as u, t_user_role as ur,t_role as r " + "where u.id = ur.user_id and r.id = ur.role_id AND u.user_name = ?"; /** * 11新建数据库和数据库表以及加入相关数据 * 22通过@Autowire注入数据源,注入数据源之前要加入依赖 * 这里使用jdbc数据源,和mysql-connection-java驱动 * 33重写方法configure(AuthenticationManagerBuilder auth) * 44准备查询语句 如上面的pwdQ和roleQ * 55使用AuthenticationManagerBuilder的jdbcAuthentication方法, * 开启jdbc的方式进行验证服务 * 66passwordEncoder设置密码的编码器。 * 注意:虽然每一次的密码的编码出来的字符串都不一样,但是数据库里面 * 只要存了一次编码之后的字符串,都可以解密 * 77usersByUsernameQuery通过查询语句返回的user pwd 布尔值,可以对用户进行验证了 * 其中布尔值就是available标明用户是否失效 * 88authoritiesByUsernameQuery通过查询语句知道用户的角色,spring security可以根据 * 查询的结果赋予权限。 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); System.out.println(passwordEncoder.encode("123456")); //虽然每一次的密码的编码出来的字符串都不一样,但是数据库里面只要存了一次编码之后的字符串,都可以解密 auth.jdbcAuthentication() .passwordEncoder(passwordEncoder) .dataSource(dataSource) .usersByUsernameQuery(pwdQ) .authoritiesByUsernameQuery(roleQ); } public static void main(String[] args) { SpringApplication.run(SpringbootsecurityApplication.class, args); } }
限制请求:
/** * 限制请求 * 默认WebSecurityConfigurerAdapte有默认的实现configure(HttpSecurity http) * 默认只要认证成功,做什么都可以 * @param http * @throws Exception */ protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); //authorizeRequests()限定只对签名成功的用户请求 //anyRequest() 限定所有请求 //authenticated()对所有签名成功的用户允许方法 //.formLogin()代表使用默认登录界面 //httpBasic()启动http基础认知 http.authorizeRequests() .anyRequest() .authenticated() .and() .formLogin() .and() .httpBasic(); }
自定义:
@Override protected void configure(HttpSecurity http) throws Exception { //authorizeRequests表示设置哪些需要签名的请求,并可以将不同请求权限赋予不同角色 http.authorizeRequests() //antMatchers配置请求路径,hasAnyRole指定角色(默认加前缀ROLE_),两个觉得哪些角色可以访问哪些路径 .antMatchers("/user/welcome").hasAnyRole("ADMIN","USER") .antMatchers("/admin/**").hasAnyRole("ADMIN") //anyRequest没有限制任何请求,permitAll表示没有设置访问权限的路径允许全部访问 .anyRequest().permitAll() //允许匿名访问 .and().anonymous() .and().formLogin() .and().httpBasic(); }
EL
/** * 使用spring表达式设置, spring EL * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/user/welcome").access("hasRole('USER') or hasRole('ADMIN')") .antMatchers("/admin/w1").access("hasAuthority('ROLE_ADMIN') && isFullyAuthenticated()") .antMatchers("/admin/w1").access("hasAuthority('ROLE_ADMIN')") .and().rememberMe() .and().formLogin() .and().httpBasic(); }
------------恢复内容开始------------
SpringSecurity的简单原理:
一旦启用了Spring Security,Spring IoC容器就会为你创建一个名称为springSecurityFilterChain 的Spring Bean。
它的类型为FilterChainProxy,事实上它也实现了Filter接口, 只是它是一个特殊的拦截器。在Spring Security 操作的
过程中它会提供Servlet 过滤器DelegatingFilterProxy , 这个过滤器会通过Spring Web IoC 容器去获取
Spring Security所自动创建的FilterChainProxy 对象,这个对象上存在一个拦截器列表( List ),列表上存在用户验证
的拦截器、跨站点请求伪造等拦截器,这样它就可以提供多种拦截功能。于是焦点又落到了FilterChainProxy 对象上,通过它
还可以注册Filter ,也就是允许注册自定义的Filter 来实现对应的拦截逻辑,以满足不同的需要。当然,Spring Security
也实现了大部分常用的安全功能,并提供了相应的机制来简化开发者的工作,所以大部分情况下并不需要自定义开发,使用它提供
的机制即可
怎么开启spring security
web工程可以使用@EnableWebSecurity来驱动spring security启动。
非web工程,使用@EnableGlobalAuthentication
实际上,@EnableWebSecurity这个注解上面已经标注了@EnableGlobalAuthentication注解
WebSecurityConfigurerAdapter中的方法:
/** * WebSecurityConfigurerAdapter虽然实现了接口WebSecurityConfigurer * 但是这个实现是空实现来的,不存在任何配置 * AuthenticationManagerBuilder定义用户,密码,角色。默认不会创建任何用户和 * 密码==有登录页面没有登录用户 */ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> { /** *配置用户签名服务,user-details机制,可以给用户赋予角色 * * @param auth 签名管理构造器,用于构建用户权限控制 * @throws Exception */ protected void configure(AuthenticationManagerBuilder auth) throws Exception { this.disableLocalConfigureAuthenticationBldr = true; } /** * 用于配置Filter链 * @param web * @throws Exception */ public void configure(WebSecurity web) throws Exception { } /** * 用于配置拦截保护请求,比如什么请求放行,什么请求验证 * HttpSecurity参数方法:指定用户和角色对对应URL的访问权限。 * @param http * @throws Exception */ protected void configure(HttpSecurity http) throws Exception { this.logger.debug("Using default configure(HttpSecurity). " + "If subclassed this will potentially override subclass configure(HttpSecurity)."); ((HttpSecurity)((HttpSecurity)((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl) http.authorizeRequests().anyRequest()).authenticated().and()) .formLogin().and()).httpBasic(); }
使用内存签名服务
package com.quan.security; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; import org.springframework.security.config.annotation.web.WebSecurityConfigurer; 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.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * springSecurit默认没有任何用户配置,springboot中,如果没有,自动生成一个 * 名称为user,密码通过随机生成,可以在日志中知道。 * 自定义用户签名服务=密码:内存签名服务,数据库签名服务,自定义签名服务 */ @SpringBootApplication //@EnableWebSecurity public class SpringbootsecurityApplication extends WebSecurityConfigurerAdapter { /** * spring5后都要求使用密码编码器。 * 11建立密码编码器实例BCryptPasswordEncoder,实现接口PasswordEncoder * 是单向不可逆的密码加密方式。 * 22inMemoryAuthentication(),这个方法将返回内存保存用户信息的管理配置InMemoryUserDetailsManagerConfigurer * 启用内存缓存机制保存用户信息 * 33passwordEncoder,设置密码编码器 * 44withUser 返回UserDetailsBuilder用户详情构造器对象 * 用于配置用户信息 * 55password,设置密码,密码设置必须是经过密码编码器加密后的字符串,使用密码登录的时候是用加密前 * 66赋予角色类型。之后通过角色赋予权限 * 备注:role方法是对authorities方法的简写,role给的角色名称实际上还会加上ROLE_,如下面的源码展示 * 77and方法表示连接,开启另一个用户的注册。 * * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String p1 = passwordEncoder.encode("hllhll"); String p2 = passwordEncoder.encode("HLLHLL"); //这里是更便捷的方式 // InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> userConfig = auth.inMemoryAuthentication().passwordEncoder(passwordEncoder); // //因为使用了authorities,所以必须加入前缀 // userConfig.withUser("quanadmin") // .password(p1).authorities("ROLE_USER","ROLE_ADMIN"); // // userConfig.withUser("quanuser") // .password(p2).authorities("ROLE_USER"); auth.inMemoryAuthentication() .passwordEncoder(passwordEncoder) .withUser("quanadmin") .password(p1) .roles("USER","ADMIN") .and() .withUser("quanuser") .password(p2) .roles("USER"); } /**InMenoryUserDetailsManagerConfigurer方法 * public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication() throws Exception { * return (InMemoryUserDetailsManagerConfigurer)this.apply(new InMemoryUserDetailsManagerConfigurer()); * } * */ /**role方法 * public User.UserBuilder roles(String... roles) { * List<GrantedAuthority> authorities = new ArrayList(roles.length); * String[] var3 = roles; * int var4 = roles.length; * * for(int var5 = 0; var5 < var4; ++var5) { * String role = var3[var5]; * Assert.isTrue(!role.startsWith("ROLE_"), () -> { * return role + " cannot start with ROLE_ (it is automatically added)"; * }); * authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); * } * * return this.authorities((Collection)authorities); * } * @param */ public static void main(String[] args) { SpringApplication.run(SpringbootsecurityApplication.class, args); } }
使用数据库定义用户认证
@SpringBootApplication //@EnableWebSecurity public class SpringbootsecurityApplication extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource = null; String pwdQ = "select user_name,pwd,available from t_user where user_name = ?"; String roleQ = "select u.user_name,r.role_name from t_user as u, t_user_role as ur,t_role as r " + "where u.id = ur.user_id and r.id = ur.role_id AND u.user_name = ?"; /** * 11新建数据库和数据库表以及加入相关数据 * 22通过@Autowire注入数据源,注入数据源之前要加入依赖 * 这里使用jdbc数据源,和mysql-connection-java驱动 * 33重写方法configure(AuthenticationManagerBuilder auth) * 44准备查询语句 如上面的pwdQ和roleQ * 55使用AuthenticationManagerBuilder的jdbcAuthentication方法, * 开启jdbc的方式进行验证服务 * 66passwordEncoder设置密码的编码器。 * 注意:虽然每一次的密码的编码出来的字符串都不一样,但是数据库里面 * 只要存了一次编码之后的字符串,都可以解密 * 77usersByUsernameQuery通过查询语句返回的user pwd 布尔值,可以对用户进行验证了 * 其中布尔值就是available标明用户是否失效 * 88authoritiesByUsernameQuery通过查询语句知道用户的角色,spring security可以根据 * 查询的结果赋予权限。 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); System.out.println(passwordEncoder.encode("123456")); //虽然每一次的密码的编码出来的字符串都不一样,但是数据库里面只要存了一次编码之后的字符串,都可以解密 auth.jdbcAuthentication() .passwordEncoder(passwordEncoder) .dataSource(dataSource) .usersByUsernameQuery(pwdQ) .authoritiesByUsernameQuery(roleQ); } public static void main(String[] args) { SpringApplication.run(SpringbootsecurityApplication.class, args); } }
限制请求:
/** * 限制请求 * 默认WebSecurityConfigurerAdapte有默认的实现configure(HttpSecurity http) * 默认只要认证成功,做什么都可以 * @param http * @throws Exception */ protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); //authorizeRequests()限定只对签名成功的用户请求 //anyRequest() 限定所有请求 //authenticated()对所有签名成功的用户允许方法 //.formLogin()代表使用默认登录界面 //httpBasic()启动http基础认知 http.authorizeRequests() .anyRequest() .authenticated() .and() .formLogin() .and() .httpBasic(); }
自定义:
@Override protected void configure(HttpSecurity http) throws Exception { //authorizeRequests表示设置哪些需要签名的请求,并可以将不同请求权限赋予不同角色 http.authorizeRequests() //antMatchers配置请求路径,hasAnyRole指定角色(默认加前缀ROLE_),两个觉得哪些角色可以访问哪些路径 .antMatchers("/user/welcome").hasAnyRole("ADMIN","USER") .antMatchers("/admin/**").hasAnyRole("ADMIN") //anyRequest没有限制任何请求,permitAll表示没有设置访问权限的路径允许全部访问 .anyRequest().permitAll() //允许匿名访问 .and().anonymous() .and().formLogin() .and().httpBasic(); }
EL
/** * 使用spring表达式设置, spring EL * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/user/welcome").access("hasRole('USER') or hasRole('ADMIN')") .antMatchers("/admin/w1").access("hasAuthority('ROLE_ADMIN') && isFullyAuthenticated()") .antMatchers("/admin/w1").access("hasAuthority('ROLE_ADMIN')") .and().rememberMe() .and().formLogin() .and().httpBasic(); }
强制使用https;
/** * springSecurit默认没有任何用户配置,springboot中,如果没有,自动生成一个 * 名称为user,密码通过随机生成,可以在日志中知道。 * 自定义用户签名服务=密码:内存签名服务,数据库签名服务,自定义签名服务 */ @SpringBootApplication @EnableWebSecurity public class SpringbootsecurityApplication extends WebSecurityConfigurerAdapter { /** * 强制使用https * 实际环境当中,信息需要谨慎的额进行保护,通过https进行加密。 * requiresChannel方法说明使用通道,requiresSecure表示https请求 * requiresInSecure取消安全请求机制 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { //设置/admin/**这个通道使用安全渠道,限定为https请求 http.requiresChannel().antMatchers("/admin/**").requiresSecure() //设置/user/**这个通道不使用安全渠道 .and().requiresChannel().antMatchers("/user/**").requiresInsecure() .and().authorizeRequests().antMatchers("/admin/**").hasAnyRole("ADMIN") .antMatchers("/user/**").hasAnyRole("USER","ADMIN"); } // @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String p1 = passwordEncoder.encode("hllhll"); String p2 = passwordEncoder.encode("HLLHLL"); auth.inMemoryAuthentication() .passwordEncoder(passwordEncoder) .withUser("quanadmin") .password(p1) .roles("USER","ADMIN") .and() .withUser("quanuser") .password(p2) .roles("USER"); }
跨站点访问请
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="./commit" method="post"> <p> 名;<input id="name" name="name" type="text" value=""/> </p> <p> 描述;<input id="describe" name="describe" type="text" value=""/> </p> <p> <input type="submit" value="提交"> </p> <input type="hidden" id="${_csrf.parameterName}" name="${_csfr.parameterName}" value="${_csrf.token}"/> </form> </body> </html> <%--隐藏的是JSTL表达式 _csrf对象是Spring提供的,启动CSRF攻击的安全认知功能后, SpringSecurity机制会生成对应的CSRF参数,他的属性parameterName 代表的是名称,token代表的是token值。都会放在form隐藏域当中。 提交的时候,提交到服务器后端,ss会进行验证这个token参数是否有效 --%>
用户认证功能:
1自定义登录界面;
@SpringBootApplication @EnableWebSecurity public class Springbootweb17Application extends WebSecurityConfigurerAdapter { /** * 自定义登录页面:登录请求连接+记住我 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { // 访问/admin下的时候需要ADMIN权限 http.authorizeRequests().antMatchers("/admin/**").access("hasRole('ADMIN')") //开启remember me功能设置token的有效期间,浏览器会使用cookie remember-me-key保存,MD5加密保存 .and().rememberMe().tokenValiditySeconds(86400).key("remember-me-key") //开启 HTTP Batic功能 .and().httpBasic() //一旦签名通过后,所有的都可以访问,注意如果不是admin那就不是全部 .and().authorizeRequests().antMatchers("/**").permitAll() //登入路径,默认登录成功跳转路径为/admin/welcome 会在webmvc配置类进行对应的登录页面设置 .and().formLogin().loginPage("/login/page").defaultSuccessUrl("/login/welcome") //登出路径,登出成功默认跳转路径/welcome .and().logout().logoutUrl("/logout/page").logoutSuccessUrl("/logout/welcome"); http.httpBasic().realmName("my-basic-name"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String p1 = passwordEncoder.encode("hllhll"); String p2 = passwordEncoder.encode("HLLHLL"); auth.inMemoryAuthentication() .passwordEncoder(passwordEncoder) .withUser("quanadmin") .password(p1) .roles("USER","ADMIN") .and() .withUser("quanuser") .password(p2) .roles("USER"); } public static void main(String[] args) { SpringApplication.run(Springbootweb17Application.class, args); } }
进行URL和jsp的映射关系配置;
@Configuration public class WebConfig implements WebMvcConfigurer { /** * 增加映射关系 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry) { //访问/login/page 映射为页面login.jsp registry.addViewController("login/page").setViewName("login"); //访问/logout 映射为logout.jsp // registry.addViewController("/logout").setViewName("logout"); //登录成功会跳转到/login/welcome 这个URL,所以这里做一个映射到logint_welcome.jsp registry.addViewController("/login/welcome").setViewName("login_welcome"); //选择登出的时候回跳转到/logout/welcome,所以这里映射到logout_welcome.jsp registry.addViewController("/logout/welcome").setViewName("logout_welcome"); } }
login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>欢迎来到傻逼无</title> </head> <%--注意:表单里面的参数名称必须是username 和password --%> <body> <form action="/login/page" method="post"> <p>名称:<input id="username" name="username" type="text" value=""></p> <p>密码:<input id="password" name="password" type="password" value=""></p> <p>记住我:<input id="remember_me" name="remember_me" type="checkbox" value=""></p> <input type="submit" value="登录"/> <%-- 表单中加入隐藏的对应参数就能避免CSRF攻击了,--%> <input type="hidden" id="${_csrf.parameterName}" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form> </body> </html>
login_welcome.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功</title>
</head>
<body>
加油,L2
<form action="/logout/page" method="post">
<input type="submit" value="登出">
<input type="hidden" id="${_csrf.parameterName}" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<%--登出的时候,也是需要验证csrf的,如果没有这个是退出错误的。--%>
</form>
</body>
</html>
logout_welcome.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登出</title> </head> <body> <h2>您已经登出系统</h2> </body> </html>