文章目录
#使用Spring Security
简介
spring security是一个提供声明式的安全访问控制解决方案的安全框架,为应用系统提供声明式的安全访问功能,减少了为企业系统安全控制编写大量重复代码的工作。
其中,spring security提供了一下功能:
身份认证
授权
加密
会话管理
Session管理
支持HTTP/HTTPS
支持Basic和Digest认证
Remember-Me
CORS
等等等等…
下面是用spring security做简单的登录检测与权限控制
场景及初始化数据
数据库有user,role,menu三个实体,其中两两之间都是多对多的关系。而根据关系数据库设计原理,多对多关系会衍生一个新表,所以,表结构大致如下
图片一
@ManytoMany的使用
@ManyToMany(fetch= FetchType.EAGER)
@JoinTable(name = "UserRole", joinColumns = { @JoinColumn(name = "userId") },
inverseJoinColumns ={@JoinColumn(name = "roleId") })
private List<Role> roleList;
其中(fetch= FetchType.EAGER)
的使用需要注意,两边只需要写一个,而不是两变都写,负责就会报错
字段的大小写
使用jpa,若有个属性为password,那么表中就是password,若表中是pass_word,那么就会报错,必须吧属性改成passWord才对。。。这个实在太坑了
MyUserDetailsService
使用数据库认证需要自定义一个类来实现UserDetailsService重写loadUserByUsername方法进行认证授权
也就是说,如果需要结合自己的数据库来进行认证,那么就必须自定义自己的DetailService,并实现loadUserByUsername。
类名前必须加上@Service注释
GrantedAuthority表示已授予的权限
//username为从前端页面接收到的数据,然后使用jpa查询数据库进行验证
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUserName(username);
System.out.println(user.getPassWord());
System.out.println(user.getUserName());
if (user == null){
throw new UsernameNotFoundException("用户不存在!");
}
//再通过SimpleGrantedAuthority聚合该用户的所有role
List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
for (Role role : user.getRoleList()) {
simpleGrantedAuthorities.add(new SimpleGrantedAuthority(role.getRoleName()));
}
//然后返回
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassWord(), simpleGrantedAuthorities);
}
SecurityConfig
SecurityConfig用来进行相关配置
首先配置资源文件,哪些可以访问,哪些需要有权限访问
//配置资源文件 其中/css/**,/index可以任意访问,/select需要USER权限,/delete需要ADMIN权限
httpSecurity
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/select").hasRole("USER")
.antMatchers("/delete").hasRole("ADMIN");
接着聚合数据库中的角色权限
//动态加载数据库中角色权限
List<Role> roleList = roleRepository.findAll();
for(Role role : roleList){
List<Menu> menuList = role.getMenuList();
for (Menu menu : menuList){
//在SpringSecurity校验权限的时候,会自动将权限前面加ROLE_,所以我们需要 将我们数据库中配置的ROLE_截取掉。
String roleName = role.getRoleName().replace("ROLE_","");
String menuName = "/" + menu.getMenuName();
httpSecurity
.authorizeRequests()
.antMatchers(menuName)
.hasRole(roleName);
}
}
然后配置登录请求,登录异常与注销登录等操作
//配置登录请求/login 登录失败请求/login_error 登录成功请求/
httpSecurity
.formLogin()
.loginPage("/login")
.failureUrl("/login_error")
.successForwardUrl("/");
//登录异常,如权限不符合 请求/401
httpSecurity
.exceptionHandling().accessDeniedPage("/401");
//注销登录 请求/logout
httpSecurity
.logout()
.logoutSuccessUrl("/logout");
}
NoOpPasswordEncoder设置密码不加密
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
configureGlobal根据自己自定义的登录证明或者在内存中加入用户来实现登录
AuthenticationManagerBuilder允许轻松构建内存身份验证
userDetailsService使用自定义的登录证明
//根据用户名密码实现登录
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
//.passwordEncoder(new BCryptPasswordEncoder())
.withUser("test").password("123").roles("USER")
.and()
.withUser("admin").password("123").roles("ADMIN","USER");
authenticationManagerBuilder.userDetailsService(myUserDetailsService);
}
@Controller和@RestController的区别
@RestController注解相当于@ResponseBody + @Controller合在一起的作用。
-
如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。
-
如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。 -
如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。
总结
一杯茶一包烟,一个小小的bug就能让你改一天,如果不明白过程或者原理,那么总有一个地方,哪怕是照着敲上去,都会出错。