Spring Security
1、认证和授权
关于认证和授权,查资料是这样解释的
认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。
授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。
先认证后授权
2、权限模块数据模型
要实现最终的权限控制,需要有一套数据模型来做支撑,一般的表结构如下:
用户表——权限表——角色表——菜单表——用户角色关系表——角色权限关系表——角色菜单关系表
类似上面这种表结构
认证的过程:只需要用户表,在登录时查询用户信息,判断用户名和密码是否正确。这一过程较为简单
授权的过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色,再根据角色查询对应的菜单,这样就确定了用户能够看到哪些菜单。然后再根据用户的角色查询对应的权限,这样就确定了用户拥有哪些权限。所以授权过程会用到上面7张表。
3、Spring Security
SS是spring提供的安全认证服务框架。使用ss可以帮助我们简化认证和授权的过程。
这是他的官网:https://docs.spring.io/spring-security/site/docs/5.4.5/reference/html5/
org.springframework.security
spring-security-web
${spring.security.version}
org.springframework.security
spring-security-config
${spring.security.version}
3.1、工程搭建
创建maven工程,打包方式为war包,把需要的依赖文件添加到pom.xml文件中。
如果需要tomcat插件,需进行以下配置:
org.apache.tomcat.maven
tomcat7-maven-plugin
85
/
提供index.html文件,提供内容Hello Spring Security!
3.2、配置web.xml
在web.xml中配置SpringMVC中的DisPatcherServlet和用于整合第三方框架的DelegatingFilterProxy,用于整合spring Security。
Archetype Created Web Application
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-security.xml
1
springmvc
*.do
?
注意:DelegatingFilterProxy用于整合第三方框架 整合Spring Security时过滤器的名称必须为springSecurityFilterChain,否则会抛出NoSuchBeanDefinitionException异常
3.3 配置 spring-security.xml
在spring-security.xml中主要配置认证规则和拦截管理器。
<?xml version="1.0" encoding="UTF-8"?>
?
3.3 运行测试
访问localhost:85/index.html
因为没有权限,所以跳转到spring security自带的登录页面。
进行登录后,重新访问
访问成功了!
4、对入门案例进行改进
前面我们已经完成了Spring Security的入门案例,通过入门案例我们可以看到,Spring Security将我们项目中的所有资源都保护了起来,要访问这些资源必须要完成认证而且需 要具有ROLE_ADMIN角色。
但是入门案例中的使用方法离我们真实生产环境还差很远,还存在如下一些问题:
1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源 不需要认证也可以访问,也就是可以匿名访问。 access="hasRole('ROLE_ADMIN')" />
2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。
3、直接将用户名和密码配置在了配置文件中,而真实生产环境下的用户名和密码往往保存在数据库中。
4、在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行 加密。
4.1配置可匿名访问的资源
因为上面的配置中“/**”代表对所有的资源都进行权限的判断,但实际生产环境中,我们并不需要将所有的页面都进行权限配置比如登录页面。
所以我们需要进行下面的配置:
然后再运行测试,发现a.html和b.html可以访问,之前的index.html任然需要登录才可以进入。
4.2 配置自己自定义的登录页面
准备工作:一个自定义的登录页面,放入相应的目录中
-
配置该页面可匿名访问
-
如果我们使用自己的登录页面,必须配置登录表单,页面提交的登录请求由框架处理
login-page="/pages/login.html":表单页面
username-parameter="username" password-parameter="password" :相对应的用户名和密码
default-target-url="/index.html" authentication-failure-url="/pages/login.html" :登录成功和失败所对应的页面
-
关闭CsrfFilter过滤器,默认开启
为什么关闭csrs过滤器,因为在使用默认的登录界面,会有一个隐藏域生成一个随机值,框架进行对比,如果不一致,会认为这是伪造的认证请求,登录操作会被禁用。
4.3从数据库查询信息(首先通过map模拟数据库)
如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个 实现UserService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。
实现代码:
先通过map集合模拟数据库查询
public class SpringSercuity implements UserDetailsService {
?
public static Map map=new HashMap<>();
static {
User user=new User();
user.setUsername("xiaohong");
user.setPassword("123456");
User user1=new User();
user1.setUsername("xiaoming");
user1.setPassword("123456");
map.put(user.getUsername(),user);
map.put(user1.getUsername(),user1);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//System.out.println(username);
User user=map.get(username);
//System.out.println(user.toString());
if (user == null){
return null;
}else {
//当前登录用户授权,后期改为数据库查询当前对应的权限
List list =new ArrayList<>();
list.add(new SimpleGrantedAuthority("permission_A")); //授予权限
list.add(new SimpleGrantedAuthority("permission_A"));
list.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //授予角色
org.springframework.security.core.userdetails.User SecurityUser=new org.springframework.security.core.userdetails.User(username,"{noop}"+user.getPassword(),list);
return SecurityUser;
}
?
}
}
4.4 对密码进行加密
前面我们使用的密码都是明文的,这是非常不安全的。一般情况下用户的密码需要进行 加密后再保存到数据库中。
常见的密码加密方式有: 3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码
MD5、SHA1:使用单向HASH算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破解
bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而 无需单独处理salt问题 加密后的格式一般为:
$2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa
加密后字符串的长度为固定的60位。其中:$是分割符,无意义;2a是bcrypt加密版本 号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了。
实现步骤:
-
配置文件中添加相关的加密对象的bean
-
自动注入
@Autowired
private BCryptPasswordEncoder passwordEncoder;
-
调用自带方法,实现密码加密
User user=new User();
user.setUsername("xiaohong");
//使用BCrypt对密码进行加密
user.setPassword(passwordEncoder.encode("admin"));
User user1=new User();
user1.setUsername("xiaoming");
user1.setPassword(passwordEncoder.encode("admin"));
4.5 配置多种校验规则
为了测试方便,首先在项目中创建a.html、b.html、c.html、d.html几个页面 修改spring-security.xml文件:
记得把上面案例用到的校验规则移除
access="hasRole('ROLE_ADMIN')==access="hasRole('ADMIN')":框架会自动加入ROLE_
4.6 注解方式权限控制
Spring Security除了可以在配置文件中配置权限校验规则,还可以使用注解方式控制类 中方法的调用。
例如Controller中的某个方法要求必须具有某个权限才可以访问,此时就可以使用Spring Security框架提供的注解方式进行控制。
实现步骤:
第一步:在spring-security.xml文件中配置组件扫描,用于扫描Controller
第二步:在spring-security.xml文件中开启权限注解支持
第三步:创建Controller类并在Controller的方法上加入注解进行权限控制
@RestController
@RequestMapping("/hello") public class HelloController {
@RequestMapping("/add")
@PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
public String add(){
System.out.println("add...");
return "success";
}
@RequestMapping("/delete")
@PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色 才能调用当前方法
public String delete(){
System.out.println("delete...");
return "success";
}
}
4.7 退出登录
用户完成登录后Spring Security框架会记录当前用户认证状态为已认证状态,即表示用 户登录成功了。那用户如何退出登录呢?
我们可以在spring-security.xml文件中进行如下配置: