01. Spring Security
参考:
https://blog.csdn.net/qq_36095679/article/details/92625701
https://blog.csdn.net/yuanlaijike/article/details/80249235
#核心组件
-
SecurityContextHolder
SecurityContextHolder它持有的是安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权等等,这些都被保存
SecurityContextHolder它持有的是安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权等等,这些都被保存
-
SecurityContext
持有Authentication对象和其他可能需要的信息
-
AuthenicationManager
AuthenticationManager 其中可以包含多个AuthenticationProvider
-
providerManager
ProviderManager对象为AuthenticationManager接口的实现类
-
AuthenticationProvider
AuthenticationProvider 主要用来进行认证操作的类 调用其中的authenticate()方法去进行认证操作
-
Authentication
Authentication:Spring Security方式的认证主体
鉴权对象,该对象主要包含了用户的详细信息(UserDetails)和用户鉴权时所需要的信息,如用户提交的用户名密码、Remember-me Token,或者digest hash值等
-
GrantedAuthority
GrantedAuthority:对认证主题的应用层面的授权,含当前用户的权限信息,通常使用角色表示
-
UserDetails
UserDetails:构建Authentication对象必须的信息,可以自定义,可能需要访问DB得到
这个接口规范了用户详细信息所拥有的字段,譬如用户名、密码、账号是否过期、是否锁定等。在Spring Security中,获取当前登录的用户的信息,一般情况是需要在这个接口上面进行扩展,用来对接自己系统的用户
-
UserDetailService
UserDetailsService:通过username构建UserDetails对象,通过loadUserByUsername根据userName获取UserDetail对象 (可以在这里基于自身业务进行自定义的实现 如通过数据库,xml,缓存获取等)
通过扩展这个接口来显示获取我们的用户信息,用户登陆时传递的用户名和密码也是通过这里这查找出来的用户名和密码进行校验,但是真正的校验不在这里,而是由AuthenticationManager以及AuthenticationProvider负责的,需要强调的是,如果用户不存在,不应返回NULL,而要抛出异常UsernameNotFoundException
#UserDetailsService
@Service("cusUserDetails")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (StringUtils.isEmpty(username)) {
throw new UsernameNotFoundException("用户名不存在");
}
UserDO user = userMapper
.selectOne(new QueryWrapper<UserDO>().eq("name", username));
if (ObjectUtils.isEmpty(user)) {
throw new UsernameNotFoundException("用户不存在");
}
//根据用户id查询用户对应的角色id
List<UserRole> userRoles = userRoleMapper
.selectList(new QueryWrapper<UserRole>().eq("user_id", user.getId()));
List<GrantedAuthority> authorities = new ArrayList<>();
for (UserRole userRole : userRoles) {
//根据用户角色id查询角色
Integer roleId = userRole.getRoleId();
Role role = roleMapper.selectById(roleId);
//添加用户权限
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new User(user.getName(), user.getPassword(), authorities);
}
}
#配置类
@Configuration
//开启@PreAuthorize()
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/").permitAll()
//如果不配置默认/login,post作为检验
// .loginProcessingUrl("/");
.and()
//默认/logout
.logout().permitAll();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/css/**","/js/**","/home.html");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
//自定义加密, 明文加密明文返回
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(charSequence.toString());
}
});
}
}
#controller
@Slf4j
@Controller
public class LoginController {
@RequestMapping("/")
public String showHome() {
//获取当前登录用户的用户名
String name = SecurityContextHolder.getContext().getAuthentication().getName();
log.info("当前登陆用户:" + name);
return "home";
}
@GetMapping("/login")
public String toLogin(){
//返回静态页面使用redirect
return "login";
}
//这里对应数据库中的权限是spring规定的格式ROLE_xxx
@ResponseBody
//需要添加@EnableGlobalMethodSecurity(prePostEnabled = true)
//@PreAuthorize用于判断用户是否有指定的权限, 没有就不能访问
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/admin")
public String printAdmin(){
return "如果你看见这句话,说明你有ROLE_ADMIN角色";
}
@RequestMapping("/user")
@ResponseBody
@PreAuthorize("hasRole('ROLE_USER')")
public String printUser() {
return "如果你看见这句话,说明你有ROLE_USER角色";
}
@GetMapping("/logout")
public String hello(){
return "redirect:/login";
}
}
#html
<!DOCTYPE html >
<html xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="stylesheet" th:href="@{/css/login.css}" type="text/css">
</head>
<body>
<form class="login-page" th:action="@{/login}" method="post">
<div class="form">
<h3>账户登录</h3>
<input type="text" placeholder="用户名" name="username" required="required"/>
<input type="password" placeholder="密码" name="password" required="required"/>
<button type="submit">登录</button>
</div>
</form>
</body>
</html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>home</title>
</head>
<body>
<h1>登陆成功</h1>
<a th:href="@{/admin}">检测ROLE_ADMIN角色</a>
<a th:href="@{/user}">检测ROLE_USER角色</a>
<button onclick="window.location.href='/logout'">退出登录</button>
</body>
</html>