Spring Security是Spring提供的一个安全框架,提供认证和授权功能,最主要的是它提供了简单的使用方式,同时又有很高的灵活性,简单,灵活,强大。
基础
SecurityContextHolder,SecurityContext,Authentication是Spring Security的基础对象。
- SecurityContextHolder:存储当前的SecurityContext,即认证用户的上下文信息,内部使用ThreadLocal。
- SecurityContext:持有Authentication对象和其他可能需要的信息
- UserDetails:从Authentication中获取的对象,代表当前用户的具体信息
- UserDetailsService:获取UserDetails的逻辑,一般封装了查询用户的逻辑,内部只有一个方法:
- .UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
- GrantedAuthority:当前用户获取到的授权信息。
在应用中获取当前用户信息:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
案例
1 Spring Boot项目中引入Spring Security的依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2 配置SpringSecurity的安全信息
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
// 只有USER权限的角色才能访问/user/接口
.antMatchers("/user/**").hasRole("USER")
.and()
.formLogin().loginPage("/login").failureUrl("/login-error");
}
// @Autowired
// public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth
// .inMemoryAuthentication()
// .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER"));
// }
// @formatter:on
// 密码明文
@Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = NoOpPasswordEncoder.getInstance();
return encoder;
}
// 获取用户的来源
@Bean
public UserDetailsService userDetailsService(){
UserDetailsService userDetailsService = new UserDetailsService(){
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (StringUtils.isEmpty(username) || !"admin".equalsIgnoreCase(username)){
return null;
}
HashSet<GrantedAuthority> hashSet = new HashSet<>();
// 内部检验权限,要以ROLE为前缀
hashSet.add(new SimpleGrantedAuthority("ROLE_USER") );
User user = new User("admin","test",hashSet);
return user;
}
};
return userDetailsService;
}
}
3 编写接口类
@Controller
public class MainController {
@RequestMapping("/")
public String root() {
return "redirect:/index";
}
@ResponseBody
@RequestMapping("/index")
public String index() {
return "当前为未经安全认证的页面";
}
@ResponseBody
@RequestMapping("/user/index")
public String userIndex() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return "user/index";
}
@RequestMapping("/login")
public String login() {
return "login";
}
@ResponseBody
@RequestMapping("/login-error")
public String loginError(Model model) {
// model.addAttribute("loginError", true);
return "login-error";
}
}
4 登录页面
// 位置 src/main/resources/templates/login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Login page</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<h1>Login page</h1>
<p>Example user: user / password</p>
<p th:if="${loginError}" class="error">Wrong user or password</p>
<form th:action="@{/login}" method="post">
<label for="username">Username</label>:
<input type="text" id="username" name="username" autofocus="autofocus" /> <br />
<label for="password">Password</label>:
<input type="password" id="password" name="password" /> <br />
<input type="submit" value="Log in" />
</form>
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
</body>
</html>
5 效果。
- 当直接访问/user/index页面的时候会因为安全配置重定向到login页面
- 我们的UserService只配置了admin/test的用户,只有用admin用户才能登录成功
- 登录成功之后再访问/user/index页面才生效
6 如果想要测试授权功能,在userDetailsService中返回结果去掉ROLE_USER权限即可。
调试关键点
org.springframework.security.access.vote.AffirmativeBased#decide 判断用户是否有权访问当前接口
org.springframework.security.authentication.dao.DaoAuthenticationProvider#additionalAuthenticationChecks 密码加密校验
最后
本文简单介绍了Spring Security的基础内容,给了一个简单的案例,自行运行查看效果
网友评论