第十三章-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();