Spring Security一些避坑指南
最近开始在项目上使用Security, 之前只是修改别人写好的,很多原理不明白。这次有幸自己搭建框架。期间是一言难尽。下面会说说一些常见的坑
关于登录验证流程的坑
Security 登录验证流程(不需要传入密码)
此处是一个流程理解问题,在security的登录过程中是采取以用户名为查询条件,重写UserDetailsService类中的loadUserByUsername(String username) 方法。
具体流程为我们的service通过继承重写此方法,以username为条件查询出userName对应的用户。
此处坑点:按照我们以往的逻辑,验证用户需要传入用户名和密码,然后查出匹配账号。然而security是先通过用户名查出用户实体,之后再通过内置的match方法比较数据库中的密码和传入的密码。(密码会通过BCryptPasswordEncoder类进行不对称加密,不用担心明文泄露)。
Security 登录成功获取用户信息(密码为NULL)
当我们在进行登录成功之后,会通过 authentication.getPrincipal()方法去获取用户信息并封装为Json返回给前台(前后端分离项目)。
此处坑点:当我们通过authentication.getPrincipal()去获取用户信息,处于安全考虑,虽然会返回password字段,但是默认字段里是null的。(不是代码问题),下图中的password为null;
Security 通过Json 返回数据
前后端项目主要是通过JSON进行数据交互,而我们在设置response时需要注意设置编码格式。不然Json返还给前台会造成乱码。
此处坑点 :不注意的话,会造成类似乱码
{
"msg": "?????",
"principal": {
"password": null,
"username": "user",
"authorities": [
{
"authority": "admin"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
}
}
设置格式类似
Map<String, Object> map = new HashMap<>();
map.put("msg", "登录成功!");
map.put("principal", authentication.getPrincipal());
resp.setContentType("application/json;charset=utf-8");
Security 会拦截Swagger2的访问
前后端项目有时会引入Swagger2进行接口对接,但是当我们通过/swagger-ui.html去访问页面时候,security会拦截我们的请求,可能机灵的小伙伴们会在config中添加允许访问路径
.antMatchers("/swagger-ui.html").permitAll();
但是,要知道swagger的页面会发送多个请求去获取接口信息,只是放开访问接口,也仅仅是显示页面全白。
此处坑点 :我们需要在config中重写个方法才能重新访问swagger2的页面数据。具体如下
/*
* 解决Security访问Swagger2被拦截的问题;
* */
@Override
public void configure(WebSecurity web) throws Exception {
// allow Swagger URL to be accessed without authentication
web.ignoring().antMatchers(
"/swagger-ui.html",
"/v2/api-docs", // swagger api json
"/swagger-resources/configuration/ui", // 用来获取支持的动作
"/swagger-resources", // 用来获取api-docs的URI
"/swagger-resources/configuration/security", // 安全选项
"/swagger-resources/**",
"/webjars/**"
);;
关于权限验证流程的坑
@PreAuthorize 不起作用,post访问404
首先我们得明白此注解用于Controller层,用于对访问的用户进行权限控制。
//类似于这样
@RestController
@RequestMapping("/role")
@PreAuthorize("hasAuthority('admin')") // 指定角色权限才能操作方法
public class RoleController {
此处坑点 : 请注意我这写的是hasAuthority(‘admin’),意思是有admin权限的用户可以访问,
各位此处需要根据自己的需求选择是权限控制还是角色控制。但是得注意如此处我是权限控制是因为我简单的写了登录权限。此处的权限名必须和hasAuthority(‘admin’)对应上。
// An highlighted block @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
//查询账号是否存在
UserEntity sysUser = queryUserByAccountId(username);
if (sysUser == null) {
return null;
}
//比较密码,注册时已经加密过
String password = passwordEncoder.encode("123");
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
//查询role角色
// List<SysRole> list = roleMapper.findRoleByUserId(sysUser.getId());
// for(SysRole role : list) {
// authorities.add(new SimpleGrantedAuthority(role.getName()));
authorities.add(new SimpleGrantedAuthority("admin"));
// }
//封装 SpringSecurity 需要的UserDetails 对象并返回
UserDetails userDetails = new User(sysUser.getUsername(), password, authorities);
return userDetails;
} catch (Exception e) {
e.printStackTrace();
//返回null即表示认证失败
return null;
}
其次,当我们开启控制层访问时,需要在配置类中开启权限访问开关
/**
* SpringSecurity配置类
* @Author xiongjian
* @CreateTime 2019/10/1 9:40
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)//这个注解是关键
public class SecurityConfig extends WebSecurityConfigurerAdapter {
关于角色和权限在security中的区别
在我们传统的模型中,角色和权限是属于包含关系的,既角色中有多个权限。但是在security中角色和权限被视为同一个对象
列举代码
//添加权限,此处注意权限和角色是不同的表 但是在security 中视为同样的
//此处添加权限
authorities.add(“某某菜单的权限”));
//此处添加的角色名为ROLE_开头
authorities.add(“ROLE_admin”));
//封装 SpringSecurity 需要的UserDetails 对象并返回.密码会自动进行匹配
UserDetails userDetails = new User(sysUser.getUsername(), sysUser.getPassword(), authorities);
写到这,就得说明下,security中角色和权限可以视为一样的,而区分两者的方法很简单,角色我们在添加时需要加上前缀ROLE_
您学废了吗?
其他内容之后再补充
希望对您有所帮助!