第十三章-SpringBoot 与安全
安全框架
- shiro
- Spring Security
认证 Authentication 建立用户,证明
授权 Authorization 访问权限
登录&认证&授权
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
控制器
package com.example.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "hello"; } }
访问http://localhost:8080/hello
没有登录会跳转到 /login
用户名:user
密码:控制台 Using generated security password
自定义用户名密码
application.properties
#自定义用户名和密码
spring.security.user.name=user
spring.security.user.password=1234567
配置认证和授权
package com.example.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //下面这两行配置表示在内存中配置了两个用户 auth.inMemoryAuthentication() .withUser("admin").roles("admin").password("$2a$10$7yOhpFTiwfkZaLRaArPJDu.Si4oWwQKmPIcnzPOOgBwstxoEh4gaK") .and() .withUser("user").roles("user").password("$2a$10$YjknRoz.3BfTOVQZbcxgN.v5w.yLrOGY8glV.IvqSBUVy29C..vU6"); } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests()//开启登录配置 .antMatchers("/hello").hasRole("admin")//表示访问 /hello 这个接口,需要具备 admin 这个角色 .anyRequest().authenticated()//表示剩余的其他接口,登录之后就能访问 .and() .formLogin() //定义登录页面,未登录时,访问一个需要登录之后才能访问的接口,会自动跳转到该页面 .loginPage("/login") // 登录处理接口 .loginProcessingUrl("/doLogin") // 定义登录时,用户名的 key,默认为 username .usernameParameter("username") // 定义登录时,用户密码的 key,默认为 password .passwordParameter("password") //登录成功的处理器 .successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException { resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); out.write("success"); out.flush(); } }) .failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOException, ServletException { resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); out.write("fail"); out.flush(); } }) .permitAll()//和表单登录相关的接口统统都直接通过 .and() .logout() .logoutUrl("/logout") .logoutSuccessHandler(new LogoutSuccessHandler() { @Override public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException { resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); out.write("logout success"); out.flush(); } }) .permitAll() .and() .httpBasic() .and() .csrf().disable(); } // 忽略拦截 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/vercode"); } }
Bcrypt 密码验证测试
package com.example.demo; import org.junit.jupiter.api.Test; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; class SecurityDemoApplicationTests { @Test void contextLoads() { String rawPassword = "123456"; // 加密 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String password = encoder.encode(rawPassword); System.out.println(password); // 比对 String encodePassword = "$2a$10$7yOhpFTiwfkZaLRaArPJDu.Si4oWwQKmPIcnzPOOgBwstxoEh4gaK"; boolean ret = encoder.matches(rawPassword, encodePassword); System.out.println(ret); } }
常用配置
// 定制请求的授权规则 http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/vip1/**").hasRole("VIP1") .antMatchers("/vip2/**").hasRole("VIP2") .antMatchers("/vip3/**").hasRole("VIP3"); // 开启登录功能 /login GET 去登录页,POST 提交登录数据 http.formLogin() // 自定义登录页 http.formLogin().loginPage("/user-login"); // 自定义登录请求参数 http.formLogin() .usernameParameter("username") .passwordParameter("password") .loginPage("/user-login"); // 开启注销功能 http.logout().logoutSuccessUrl("/") // 记住我,将用户信息保存在cookie中 http.remenberMe();