前面我们已经完成了传智健康后台管理系统的部分功能,例如检查项管理、检查组管理、套餐管理、预 约设置等。接下来我们需要思考2个问题:
问题1:在生产环境下我们如果不登录后台系统就可以完成这些功能操作吗? 答案显然是否定的,要操作这些功能必须首先登录到系统才可以。
问题2:是不是所有用户,只要登录成功就都可以操作所有功能呢? 答案是否定的,并不是所有的用户都可以操作这些功能。
不同的用户可能拥有不同的权限,这就需要进 行授权了。
认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认 证的目的是让系统知道你是谁。
授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。 本章节就是要对后台系统进行权限控制,其本质就是对用户进行认证和授权。
Spring Security是 Spring提供的安全认证服务的框架。 使用Spring Security可以帮助我们来简化认证 和授权的过程。
官网:https://spring.io/projects/spring-security
对应的maven坐标:
org.springframework.security
spring-security-web
5.0.5.RELEASE
org.springframework.security
spring-security-config
5.0.5.RELEASE
常用的权限框架除了Spring Security,还有Apache的shiro框架。
自己添加一下版本
com.github.pagehelper
pagehelper
org.mybatis
mybatis
org.mybatis
mybatis-spring
com.github.miemiedev
mybatis-paginator
mysql
mysql-connector-java
com.alibaba
druid
commons-fileupload
commons-fileupload
org.springframework
spring-context
org.springframework
spring-beans
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-jdbc
org.springframework
spring-aspects
org.springframework
spring-jms
org.springframework
spring-context-support
org.springframework
spring-test
com.alibaba
dubbo
org.apache.zookeeper
zookeeper
com.github.sgroschupf
zkclient
junit
junit
com.alibaba
fastjson
javassist
javassist
commons-codec
commons-codec
org.apache.poi
poi
redis.clients
jedis
com.qiniu
qiniu-java-sdk
com.sun.jersey
jersey-client
org.apache.poi
poi-ooxml
org.springframework.security
spring-security-web
org.springframework.security
spring-security-config
org.springframework.security
spring-security-taglibs
com.aliyun
aliyun-java-sdk-core
3.3.1
com.aliyun
aliyun-java-sdk-dysmsapi
1.0.0
org.freemarker
freemarker
2.3.23
org.apache.tomcat.maven
tomcat7-maven-plugin
85
/
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
准备一个index.html 页面
Title
hello ! welcome securty!!!
启动tomcat --》
登录后访问index.html页面即可!!! 此登录页面是security自带的!!!
更加详细的入门案例可参考:
https://blog.csdn.net/qq_45441466/article/details/109371359
前面我们已经完成了Spring Security的入门案例,通过入门案例我们可以看到,Spring Security将我们 项目中的所有资源都保护了起来,要访问这些资源必须要完成认证而且需要具有ROLE_ADMIN角色。
但是入门案例中的使用方法离我们真实生产环境还差很远,还存在如下一些问题:
1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源不需要认证也 可以访问,也就是可以匿名访问。
2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。
3、在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行加密。
本章节需要对这些问题进行改进。
第一步:在项目中创建pages目录,在pages目录中创建a.html和b.html
第二步:在spring-security.xml文件中配置,指定哪些资源可以匿名访问
即是当前pages目录下的a.html 和 b.html 页面进行权限校验
也可以配置pages/** 放在pages目录下所有资源不校验权限
通过上面的配置可以发现,pages目录下的文件可以在没有认证的情况下任意访问。
第一步:提供login.html作为项目的登录页面
Title
第二步:修改spring-security.xml文件,指定login.html页面可以匿名访问
第三步:修改spring-security.xml文件,加入表单登录信息的配置
配置在
第四步:修改spring-security.xml文件,关闭CsrfFilter过滤器
csrf: spring security 用于防止跨域攻击的!!
如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个实现 UserDetailsService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法 并自动进行密码校验。
实现类代码:
package com.itheima.service;
import com.itheima.pojo.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author panghl
* @Date 2021/5/3 14:34
* @Description TODO
**/
public class SpringSecurityUserService implements UserDetailsService {
public static Map userMap = new HashMap<>();
static {
User user1 = new User();
user1.setUsername("xiaoming");
user1.setPassword("12345");
User user2 = new User();
user2.setUsername("admin");
user2.setPassword("admin");
userMap.put(user1.getUsername(), user1);
userMap.put(user2.getUsername(), user2);
}
/**
* 根据用户名查询用户信息
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("用户输入的用户名为:" + username);
// 根据用户名查询数据库获得用户信息(包含数据库中存储的密码信息) --static代码块模拟了数据
User user = userMap.get(username);
if (user == null) {
//用户名不存在
return null;
} else {
// 将用户信息返回给框架
// 框架会进行密码比对(页面提交的密码和数据库中查询的密码进行比对)
List list = new ArrayList<>();
//为当前登录用户授权,后期需要改为从数据库查询当前用户对应的权限
list.add(new SimpleGrantedAuthority("permission_a"));//授予权限
list.add(new SimpleGrantedAuthority("permission_b"));
if (username.equals("admin")) {
//用户名为admin 才授予ADMIN角色权限--即可登录
list.add(new SimpleGrantedAuthority("ROLE_ADMIN"));//授予角色
}
org.springframework.security.core.userdetails.User securityUser =
new org.springframework.security.core.userdetails.User(username, "{noop}" + userMap.get(username).getPassword(),
true, true, true, true, list);
return securityUser;
}
}
}
重启tomcat测试可知:admin拥有ADMIN角色可以登录,而xiaoming则没有ADMIN角色不能登录!
详细查数据库玩法可看:https://blog.csdn.net/qq_45441466/article/details/109372046
前面我们使用的密码都是明文的,这是非常不安全的。一般情况下用户的密码需要进行加密后再保存到 数据库中。
常见的密码加密方式有:
3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码
MD5、SHA1:使用单向HASH算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破 解
bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理 salt问题
加密后的格式一般为:
$2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa
加密后字符串的长度为固定的60位。其中:$是分隔符,无意义;2a是bcrypt加密版本号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了。
第一步:在spring-security.xml文件中指定密码加密对象
第二步:修改UserService实现类
package com.itheima.service;
import com.itheima.pojo.User;
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.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author panghl
* @Date 2021/5/3 14:34
* @Description TODO
**/
public class SpringSecurityUserService2 implements UserDetailsService {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
public Map userMap = new HashMap<>();
public void initUserData() {
User user1 = new User();
user1.setUsername("xiaoming");
user1.setPassword(passwordEncoder.encode("12345"));
User user2 = new User();
user2.setUsername("admin");
user2.setPassword(passwordEncoder.encode("admin"));//使用bcrypt提供的方法对密码加密
userMap.put(user1.getUsername(), user1);
userMap.put(user2.getUsername(), user2);
}
/**
* 根据用户名查询用户信息
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("用户输入的用户名为-->2-->:" + username);
this.initUserData();
// 根据用户名查询数据库获得用户信息(包含数据库中存储的密码信息) --static代码块模拟了数据
User user = userMap.get(username);
if (user == null) {
//用户名不存在
return null;
} else {
// 将用户信息返回给框架
// 框架会进行密码比对(页面提交的密码和数据库中查询的密码进行比对)
List list = new ArrayList<>();
//为当前登录用户授权,后期需要改为从数据库查询当前用户对应的权限
list.add(new SimpleGrantedAuthority("permission_a"));//授予权限
list.add(new SimpleGrantedAuthority("permission_b"));
if (username.equals("admin")) {
//用户名为admin 才授予ADMIN角色权限--即可登录
list.add(new SimpleGrantedAuthority("ROLE_ADMIN"));//授予角色
}
org.springframework.security.core.userdetails.User securityUser =
new org.springframework.security.core.userdetails.User(username,user.getPassword(),
true, true, true, true, list);
return securityUser;
}
}
}
通过debug可以发现密码以及通过bcrypt方式加密了!
补充:
对称算法:把原文A计算为密文B。同时,使用算法的逆运算,还可以把密文B还原为原文A。一般用于信息传输加密。
非对称算法: 一般是一些摘要算法,算法不可逆。即使知道算法的全部内容,也无法还原原文。
原文A计算为密文B,密文B不包含原文的所有信息,仅仅是使用原文计算出来的一个单独字符串,无法还原。
为了测试方便,首先在项目中创建a.html、b.html、c.html、d.html几个页面
修改spring-security.xml文件
修改实现类代码:
list.add(new SimpleGrantedAuthority("add"));//授予权限
测试可知:
xiaoming通过认证就可以访问index.jsp 和 pages目录下的a.html
而admin用户通过认证可以访问pages目录下的所有html页面。
SpringSecurity除了可以在配置文件中配置权限校验规则,还可以使用注解方式控制类中方法的调用。例如:Controller中的某个方法要求必须具有某个权限才能可以访问,此时就可以使用Spring Security框架提供的注解方式进行控制
实现步骤:
第一步:在spring-security.xml文件中配置组件扫描,用于扫描Controller
第二步:在spring-security.xml文件中开启权限注解支持
第三步:创建Controller类并在Controller的方法上加入注解进行权限控制
package com.itheima.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author panghl
* @Date 2021/5/3 16:13
* @Description TODO
**/
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/add")
@PreAuthorize("hasAuthority('add')") //调用此方法要求当前用户具有add权限
public String add(){
System.out.println("add....");
return "success-add";
}
@RequestMapping("/delete")
@PreAuthorize("hasRole('ROLE_ADMIN')") //调用此方法要求当前用户具有ROLE_ADMIN角色
public String delete(){
System.out.println("delete....");
return "success-delete";
}
}
通过测试可知:
xiaoming是无法访问add / delete方法的,admin是可以操作这两个方法的!!!
spring-security实现方法级别、@Secured注解、JSR-250注解、页面端的权限控制:https://blog.csdn.net/qq_45441466/article/details/109558624
用户完成登录后SpringSecurity框架会记录当前用户认证状态为已认证状态,即表示用户登录成功了。那用户如何退出登录呢?我们可以在spring-security.xml文件中进行如下配置:
通过上面的配置可以发现,如果用户要退出登录,只需要请求/logout.do这个URL地址就可以,同时会将当前session失效,最后页面会跳转到login页面。
想要获取黑马、尚硅谷等资源课程、资料、项目实战等等更多资源。可加群:1164437770(期待您的加入一起讨论)