单体架构SpringSecurity认证中心
一:什么是认证授权
认证是对身份的确认,授权是对权限的控制,所有的认证授权都是基于RBAC基于角色的权限控制模型,权限与角色关联,用户与角色关联
二:什么是SpringSecurity
SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,减少了为企业系统安全控制编写的大量重复代码
三:Security认证入门
3.1引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.2编写认证成功后的跳转器
@Controller
public class AuthController {
//登录成功后重定向地址
@RequestMapping("/loginSuccess")
@ResponseBody
public String loginSuccess(){
return "登录成功";
}
}
3.3配置SpringSecurity
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//提供用户信息,这里没有从数据库查询用户信息,在内存中模拟
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager inMemoryUserDetailsManager =
new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(User.withUsername("zs").password("123").authorities("admin").build());
return inMemoryUserDetailsManager;
}
//密码编码器:不加密
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//授权规则配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //授权配置
.antMatchers("/login").permitAll() //登录路径放行
.anyRequest().authenticated() //其他路径都要认证之后才能访问
.and().formLogin() //允许表单登录
.successForwardUrl("/loginSuccess") // 设置登陆成功页
.and().logout().permitAll() //登出路径放行 /logout
.and().csrf().disable(); //关闭跨域伪造检查
}
}
3.4直接访问服务端口号+login,输入用户名zs,密码123即可登录成功,返回登录成功后的跳转器的内容
3.5上面是将用户信息写死在内存中进行模拟的,我们可以直接向数据库查询
3.5.1注释掉配置类中的用户信息方法,自定义一个类去实现UserDetailsService接口,重写loadUserByUsername方法,在这个方法中向数据库中进行查询,需要返回一个User对象,传入用户名、密码、权限集合即可
package cn.ybl.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//提供用户信息,这里没有从数据库查询用户信息,在内存中模拟
// @Bean
// public UserDetailsService userDetailsService(){
// InMemoryUserDetailsManager inMemoryUserDetailsManager =
// new InMemoryUserDetailsManager();
// inMemoryUserDetailsManager.createUser(User.withUsername("zs").password("123").authorities("admin").build());
// return inMemoryUserDetailsManager;
// }
//测试
public static void main(String[] args) {
BCryptPasswordEncoder encryption = new BCryptPasswordEncoder();
//加密,每次加密得到的结果不一样,但是底层会自动进行对比,不影响
String encode = encryption.encode("123");
String encode1 = encryption.encode("123");
System.out.println(encode1);
System.out.println(encode);
//对比密码和密码加密字符串
boolean matches = encryption.matches("123", "$2a$10$SLdM4KRwriqmYqEQZbDJveEjHu0EBOcrYPib893E/vtAARczJ2h0W");
System.out.println(matches);
}
//指定密码编码器进行加密密码:BCryptPasswordEncoder
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//授权规则配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //授权配置
.antMatchers("/login").permitAll() //登录路径放行
.anyRequest().authenticated() //其他路径都要认证之后才能访问
.and().formLogin() //允许表单登录
.successForwardUrl("/loginSuccess") // 设置登陆成功页
.and().logout().permitAll() //登出路径放行 /logout
.and().csrf().disable(); //关闭跨域伪造检查
}
}
package cn.ybl.userDetail;
import cn.ybl.domain.Login;
import cn.ybl.domain.LoginLog;
import cn.ybl.domain.Permission;
import cn.ybl.enums.GlobalErrorCode;
import cn.ybl.service.ILoginLogService;
import cn.ybl.service.ILoginService;
import cn.ybl.service.IPermissionService;
import cn.ybl.util.AssertUtil;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author Mr.Yang
* @Date 2022/9/23 11:54
* @description
*/
@Component
public class MyUserDetailServiceImpl implements UserDetailsService {
@Autowired
private ILoginService loginService;
@Autowired
private IPermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Wrapper<Login> wrapper = new EntityWrapper<>();
wrapper.eq("username",username);
Login login = loginService.selectOne(wrapper);
//登录用户不存在
AssertUtil.isNotNull(login, GlobalErrorCode.LOGIN_USER_EMPTY);
//查询当前登录用户的权限
List<Permission> permissions = permissionService.loadAuthority(login.getId());
//将对象转为权限字段的集合
List<GrantedAuthority> authorities = permissions.stream().map(p -> new SimpleGrantedAuthority(p.getSn())).collect(Collectors.toList());
return new User(
login.getUsername(),
login.getPassword(),
authorities);
}
}
四:Security授权入门
4.1web授权
//动态添加授权:从数据库动态查询出,哪些资源需要什么样的权限
List<Permission> permissions = loginMapper.listPermissions();
for(Permission permission : permissions){
//如: /employee/list 需要 employee:list 权限才能访问
http.authorizeRequests().antMatchers(permission.getResource()).hasAuthority(permission.getSn());
}
4.2方法授权
在配置类上开启注解:
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled= true)
在受限的接口上打上注解:
@PreAuthorize("hasAnyAuthority('employee:add','employee:update')")
@RequestMapping("/employee/add") //要访问employee/add就必须有employee:add或者employee:update其中一个权限
public String add(){
return "employee.add";
}