网站建设丿金手指下拉,网站建设网站维护,深圳网站建设公司是,网站开发报价合同Spring Security概述
1.什么是Spring Security?
Spring Security是一个Java框架#xff0c;用于保护应用程序的安全性。它提供了一套全面的安全解决方案#xff0c;包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念#xff0c;可以轻松地集成到任…Spring Security概述
1.什么是Spring Security?
Spring Security是一个Java框架用于保护应用程序的安全性。它提供了一套全面的安全解决方案包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念可以轻松地集成到任何基于Spring的应用程序中。它支持多种身份验证选项和授权策略开发人员可以根据需要选择适合的方式。此外Spring Security还提供了一些附加功能如集成第三方身份验证提供商和单点登录以及会话管理和密码编码等。总之Spring Security是一个强大且易于使用的框架可以帮助开发人员提高应用程序的安全性和可靠性。 什么是授权
这个根据用户的权限来控制用户使用资源的过程就是授权。
为什么要授权
认证是为了保护用户身份的合法性授权则是为了更细的粒度的对隐私数据经行划分授权是在认证通过后发生的控制不同的用户能够访问不同的资源
授权授权是用户认证通过根据用户的权限来控制用户访问资源的过程拥有资源的访问权限则正常访问没有权限则拒绝访问 Spring Security入门体验
一.快速搭建Spring Security安全框架项目
1.1--创建一个SpringBoot项目 1.2--修改SpringBoot的版本号并修改jdk版本号
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.example/groupIdartifactIdSpringSecurity001/artifactIdversion0.0.1-SNAPSHOT/versionnameSpringSecurity001/namedescriptionSpringSecurity001/descriptionpropertiesjava.version1.8/java.versionproject.build.sourceEncodingUTF-8/project.build.sourceEncodingproject.reporting.outputEncodingUTF-8/project.reporting.outputEncodingspring-boot.version2.6.13/spring-boot.version!--SpringBoot版本号 这里使用的JDK版本为8--/propertiesdependencies!--引入Spring Security安全框架依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--添加lombok依赖 实体类的get set方法生成--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency!--测试依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-dependencies/artifactIdversion${spring-boot.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdversion3.8.1/versionconfigurationsource1.8/sourcetarget1.8/targetencodingUTF-8/encoding/configuration/pluginplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdversion${spring-boot.version}/versionconfigurationmainClasscom.aaa.SpringSecurity001Application/mainClassskiptrue/skip/configurationexecutionsexecutionidrepackage/idgoalsgoalrepackage/goal/goals/execution/executions/plugin/plugins/build/project1.3--引入SPringleSecurity依赖
!--引入Spring Security安全框架依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependency
1.4--创建一个HelloControllerController层定义一个资源 1.5--启动项目并访问资源路径
访问路径http://localhost:8080/hello 注我们发现使用了security后在访问我们自己的接口security会拦截并跳转到认证页面认证后才可以访问。默认认证的账号uer密码在控制台 二.创建Spring Security自定义账户和密码
我们刚才使用的是security自带的账户和密码我们自己也可以自定义账户和密码;
2.1--在application.properties配置文件中定义一个账户和密码 自定义账户和密码文件如下
#创建Spring Security自定义账户和密码#账户
spring.security.user.nameadmin
#密码
spring.security.user.password123456
server.port8080注配置完自定义文件和密码重启运行这是控制台中没有密码控制台不会生成密码 注这种方式只能定义一个账户和密码无法定义多个账户和密码
三.自定义多个账户和密码
在配置文件中只能定义一个账户和密码。我们可以定义一个配置类完成多个账号和密码的定义
1.创建Config层 MysecurityConfig;
配置类内容
package com.aaa.config;import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() //基于内存创建的账户和密码 未来可以用来连接数据库//账户.withUser(zhangsan)//密码.password(123456)//用户具备的角色.roles(admin).and() //连接符.withUser(lisi).password(012345).roles(test).and().withUser(wangwu).password(567890).roles(group);}
}注如果使用了配置类那么之前在配置文件中定义的账户和密码不在生效
控制台错误提示需要给它指定的加密器 解决方式添加加密器
//可以把该方法返回的对象交于Spring容器管理Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
package com.aaa.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {//可以把该方法返回的对象交于Spring容器管理Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() //基于内存创建的账户和密码 未来可以用来连接数据库//账户.withUser(zhangsan)//密码.password(passwordEncoder().encode(123456))//用户具备的角色.roles(admin).and() //连接符.withUser(lisi).password(passwordEncoder().encode(012345)).roles(test).and().withUser(wangwu).password(passwordEncoder().encode(567890)).roles(group);}
}注把明文密码通过加密器经行加密
再次访问资源 四.密码加密器
密码加密器可以把把明文转换为密文 encode
同时也可以匹配密码 matches
package com.aaa.Test;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;public class test {public static void main(String[] args) {//创建加密器对象PasswordEncoder passwordEncoder new BCryptPasswordEncoder();//使用加密器String encode passwordEncoder.encode(123456);System.out.println(encode);//匹配密码 第一个参数是客户输得密码 第二个参数是加密器密文//把客户输得密码和加密过的密码经行比对是否一致一致返回true不一致返回flaseboolean matches passwordEncoder.matches(123456, encode);//结果为trueSystem.out.println(matches); }
}问题对称加密和非对称加密
答对称加密加密和解密使用的密钥是同一个 --这种加密的方式可以破解 非对称加密 加密和解密使用的密钥不是同一个--这种加密的方式无法破解 五.获取当前用户的信息
获取登录成功者的信息
package com.aaa.Controller;import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;RestController
public class HelloController {GetMapping(/hello)public String hello(){return hello资源·······;}GetMapping(/info)public Authentication info(){//把当前信息保存到SecurityContext类中;SecurityContext securityContext SecurityContextHolder.getContext();//把当前用户封装到Authentication类中 账户 密码 权限 角色等信息Authentication authentication securityContext.getAuthentication();return authentication;}
}Security框架会把当前用户信息封装到Authentication中并把该类对象存放到SecurityContext中
访问资源路径http://localhost:8080/info 六.修改它的登录页面
默认security提供了一个登录页面如果不想使用它提供的页面我们可以指定我们自己的登录页面
1.自己在static静态资源文件下创建一个登录页面
!DOCTYPE html
html langen
headmeta charsetUTF-8title登录页/titlestylebody {background-color: #222;font-family: Arial, sans-serif;color: #fff;display: flex;align-items: center;justify-content: center;height: 100vh;}.login-container {background-color: #333;padding: 40px;border-radius: 10px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);}h1 {text-align: center;}.form-group {margin-bottom: 20px;}.form-group label {display: block;margin-bottom: 5px;}.form-group input[typetext],.form-group input[typepassword] {width: 100%;padding: 10px;border-radius: 5px;border: none;background-color: #555;color: #fff;}.form-group input[typesubmit] {width: 100%;padding: 10px;border-radius: 5px;border: none;background-color: #007bff;color: #fff;cursor: pointer;transition: background-color 0.3s ease;}.form-group input[typesubmit]:hover {background-color: #0056b3;}/style
/head
body
div classlogin-containerh1Welcome to Cool Login Page/h1form actionlogin methodpostdiv classform-grouplabel forusernameUsername:/label!--账户--input typetext idusername nameusername required/divdiv classform-grouplabel forpasswordPassword:/label!--密码--input typepassword idpassword namepassword required/divdiv classform-groupinput typesubmit valueLogin/div/form
/div
/body
/html
2.修改security配置类在config层 HelloConfig文件中重写configure方法 Overrideprotected void configure(HttpSecurity http) throws Exception {//设置表单登录信息http.formLogin()//设置自己的登录页面.loginPage(/login.html)//设置登录表单的提交路径 要和login.html中的action一致.loginProcessingUrl(/login)//这个页面允许放行.permitAll();//禁止跨域伪造请求验证http.csrf().disable();//其他请求路径都要认证http.authorizeRequests().anyRequest().authenticated();}
访问路径访问info或者hello都可以进入到自己的登录页面 七.设置跳转登录成功页面
默认登录成功 / 或原来的访问路径 创建SuccessController
package com.aaa.Controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;Controller
public class SuccessController {PostMapping(/success)public String success(){return redirect:/success.html;}
}创建一个success.html登录成功页面
!DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/title
/head
body
h1登录成功/h1
/body
/html 输入账号和密码
登录成功 授权的实现
授权用户具有的权限和资源绑定的过程就是授权
第一步修改config MySecurityConfig配置文件 第二步创建自定义资源访问 controller层 AuthController资源文件
package com.aaa.Controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;//资源
RestController
public class AuthController {GetMapping(select)public String select(){System.out.println(查询资源);return 查询资源;}GetMapping(update)public String update(){System.out.println(修改资源);return 修改资源;}GetMapping(delete)public String delete(){System.out.println(删除资源);return 删除资源;}GetMapping(xxx)public String xxx(){System.out.println(共享资源);return 共享资源;}
}第三步: 权限和资源进行绑定 config MySecurityConfig配置文件 package com.aaa.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {//可以把该方法返回的对象交于Spring容器管理Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}Overrideprotected void configure(HttpSecurity http) throws Exception {//设置表单登录信息http.formLogin()//设置自己的登录页面.loginPage(/login.html)//设置登录表单的提交路径 要和login.html中的action一致.loginProcessingUrl(/login)//表示登录成功跳转路径 提交方式必须为post请求.successForwardUrl(/success)//这个页面允许放行.permitAll();//设定资源和权限进行绑定http.authorizeRequests().antMatchers(/select).hasAnyAuthority(user:select).antMatchers(/update).hasAnyAuthority(user:update).antMatchers(/delect).hasAnyAuthority(user:delect).antMatchers(/xxx).hasAnyAuthority(user:xxx);//异常处理页面http.exceptionHandling().accessDeniedPage(/403.html);//禁止跨域伪造请求验证http.csrf().disable();//其他请求路径都要认证http.authorizeRequests().anyRequest().authenticated();}Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() //基于内存创建的账户和密码 未来可以用来连接数据库//账户.withUser(zhangsan)//密码.password(passwordEncoder().encode(123456))//用户具备的角色.roles(admin)//设定用户权限.authorities(user:select,user:update,user:delete).and() //连接符.withUser(lisi).password(passwordEncoder().encode(012345)).roles(test).authorities(user:xxx).and().withUser(wangwu).password(passwordEncoder().encode(567890)).roles(group);}
}第四步访问资源 上面再访问没有的权限资源时出现上面的错误界面这种界面对客户友好。跳转到一个权限不足的界面 使用security注解完成授权
思考: 上面权限和资源得到绑定 需要手动一一绑定。真正再开发中我们具有的权限和资源是非常多的。如果手动一一绑定是很麻烦的。
第一步开启security权限注解驱动 第二步再相应的资源上使用注解
controller层 AuthController资源文件 package com.aaa.Controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;//资源
RestController
public class AuthController {GetMapping(select)PreAuthorize(value hasAuthority(user:select))public String select(){System.out.println(查询资源);return 查询资源;}GetMapping(update)PreAuthorize(value hasAuthority(user:upate))public String update(){System.out.println(修改资源);return 修改资源;}GetMapping(delete)PreAuthorize(value hasAuthority(user:delete))public String delete(){System.out.println(删除资源);return 删除资源;}GetMapping(xxx)PreAuthorize(value hasAuthority(user:xxx))public String xxx(){System.out.println(共享资源);return 共享资源;}
}第三步修改security配置 第四步访问资源流程是一样的 略 了解security认证的流程(源码)
核心过滤器:UsernamePasswordAuthenticationFilter 使用Spring Security连接数据库自定义认证--需要连接数据库
第一步创建securtiy数据库
DROP TABLE IF EXISTS sys_permission;
CREATE TABLE sys_permission (perid int(0) NOT NULL AUTO_INCREMENT,pername varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,percode varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,PRIMARY KEY (perid) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 5 CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci ROW_FORMAT Dynamic;-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO sys_permission VALUES (1, 用户查询, user:query);
INSERT INTO sys_permission VALUES (2, 用户添加, user:add);
INSERT INTO sys_permission VALUES (3, 用户修改, user:update);
INSERT INTO sys_permission VALUES (4, 用户删除, user:delete);
INSERT INTO sys_permission VALUES (5, 用户导出, user:export);-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS sys_role;
CREATE TABLE sys_role (roleid int(0) NOT NULL AUTO_INCREMENT,rolename varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,PRIMARY KEY (roleid) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 3 CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci ROW_FORMAT Dynamic;-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO sys_role VALUES (1, 管理员);
INSERT INTO sys_role VALUES (2, 测试人员);
INSERT INTO sys_role VALUES (3, 普通用户);-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS sys_role_permission;
CREATE TABLE sys_role_permission (perid int(0) NULL DEFAULT NULL,roleid int(0) NULL DEFAULT NULL
) ENGINE InnoDB CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci ROW_FORMAT Dynamic;-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO sys_role_permission VALUES (2, 1);
INSERT INTO sys_role_permission VALUES (1, 1);
INSERT INTO sys_role_permission VALUES (3, 1);
INSERT INTO sys_role_permission VALUES (4, 1);
INSERT INTO sys_role_permission VALUES (2, 2);
INSERT INTO sys_role_permission VALUES (1, 2);
INSERT INTO sys_role_permission VALUES (3, 2);
INSERT INTO sys_role_permission VALUES (1, 3);
INSERT INTO sys_role_permission VALUES (5, 3);-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS sys_user;
CREATE TABLE sys_user (userid int(0) NOT NULL AUTO_INCREMENT,username varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,userpwd varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,sex varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,address varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,PRIMARY KEY (userid) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 3 CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci ROW_FORMAT Dynamic;-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO sys_user VALUES (1, 张三, $2a$10$cI7e7bgSs9.9nNHhxKO9LuK/Ll.AeZwgUyZb77oD2y3UwwZyZhWG6, 男, 郑州);
INSERT INTO sys_user VALUES (2, 李四, $2a$10$cI7e7bgSs9.9nNHhxKO9LuK/Ll.AeZwgUyZb77oD2y3UwwZyZhWG6, 男, 北京);
INSERT INTO sys_user VALUES (3, 王五, $2a$10$cI7e7bgSs9.9nNHhxKO9LuK/Ll.AeZwgUyZb77oD2y3UwwZyZhWG6, 女, 杭州);-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS sys_user_role;
CREATE TABLE sys_user_role (userid int(0) NOT NULL,roleid int(0) NULL DEFAULT NULL
) ENGINE InnoDB CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci ROW_FORMAT Dynamic;-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO sys_user_role VALUES (1, 1);
INSERT INTO sys_user_role VALUES (2, 2);
INSERT INTO sys_user_role VALUES (3, 3);
INSERT INTO sys_user_role VALUES (1, 2);SET FOREIGN_KEY_CHECKS 1;第二步创建一个SpringBoot项目--------引入相关的依赖lombok依赖 mysql驱动依赖 mybatis-plus依赖security启动依赖 dependencies!--加入security安全框架依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependency!--lombok依赖--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency!--mysql驱动依赖--dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependency!--mybatis-plus依赖--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.4/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency/dependencies
第三步创建实体类 entity层 User Role Permission User实体类
package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;Data
TableName(sys_user) //实体类和数据库表名不一致时使用该注解进行映射
public class User {TableId(type IdType.AUTO) //主键自增private Integer userid;private String username;private String userpwd;private String sex;private String address;
}Role实体类
package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;Data
TableName(sys_role) //实体类和数据库表名不一致时使用该注解进行映射
public class Role {TableId(type IdType.AUTO) //主键自增private Integer roleid;private String rolename;
}Permission实体类
package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;Data
TableName(sys_permission) //实体类和数据库表名不一致时使用该注解进行映射
public class Permission {TableId(type IdType.AUTO) //主键自增private Integer perid;private String pername;private String percode;
}第四步创建相应的Dao接口 Dao层 UserDao RoleDao PermissionDao UserDao接口
package com.aaa.dao;import com.aaa.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface UserDao extends BaseMapperUser {}ReloDao接口
package com.aaa.dao;import com.aaa.entity.Role;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface RoleDao extends BaseMapperRole {
}PermissionDao接口
package com.aaa.dao;import com.aaa.entity.Permission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;import java.util.List;public interface PermissionDao extends BaseMapperPermission {//查找用户的权限Select(select distinct p.* from sys_user_role u join sys_role_permission r on u.roleidr.roleid join sys_permission p on r.peridp.perid where userid#{userId})public ListPermission selectByUserId(Integer userId);}第五步创建业务层 Service MyUserDetailService代码
package com.aaa.service;import com.aaa.dao.PermissionDao;
import com.aaa.dao.UserDao;
import com.aaa.entity.Permission;
import com.aaa.entity.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Service;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;Service
public class MyUserDetailService implements UserDetailsService {Autowiredprivate UserDao userDao;Autowiredprivate PermissionDao permissionDao;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//1. 根据账户查找用户信息QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.eq(username,username);User user userDao.selectOne(queryWrapper);//2.判断用户是否为nullif(user!null){//3.查找用户的具体权限ListPermission permissions permissionDao.selectByUserId(user.getUserid());/*** String username, 账户* String password, 密码--而是数据库中存在的密码* Collection? extends GrantedAuthority authorities 当前用户具有的权限*/CollectionSimpleGrantedAuthority authorities new ArrayList();//把ListPermission转化为CollectionSimpleGrantedAuthorityfor(Permission per:permissions){SimpleGrantedAuthority simpleGrantedAuthoritynew SimpleGrantedAuthority(per.getPercode());authorities.add(simpleGrantedAuthority);}org.springframework.security.core.userdetails.User userDetailnew org.springframework.security.core.userdetails.User(user.getUsername(),user.getUserpwd(),authorities);return userDetail;}return null;}
}第六步:创建Security配置文件 config层 mySecurityConfig文件
package com.aaa.config;import com.aaa.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate MyUserDetailService userDetailService;//添加加密器Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//使用数据库账号和密码auth.userDetailsService(userDetailService);}Overrideprotected void configure(HttpSecurity http) throws Exception {//设置表单信息//使用自己的前端登录页面http.formLogin().loginPage(/login.html).loginProcessingUrl(/login).successForwardUrl(/success).permitAll();//异常处理页面http.exceptionHandling().accessDeniedPage(/403.html);//禁用跨域伪造响应http.csrf().disable();//其他所有路径通过都需要认证http.authorizeRequests().anyRequest().authenticated();}
}第七步配置application.properties文件连接数据库
server.port8080
spring.datasource.urljdbc:mysql://localhost:3306/security
spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver
spring.datasource.usernameroot
spring.datasource.passwordroot
第八步在SpringSecurity002Application 启动类中添加注解让其扫描到com.aaa.dao层
package com;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
//添加注解 包扫描到dao层MapperScan(basePackages com.aaa.dao)
public class SpringSecurity002Application {public static void main(String[] args) {SpringApplication.run(SpringSecurity002Application.class, args);}}第九步访问资源 使用security认证授权----完成前后端分离的模式
熟悉前后端分离 JWT概述
1.什么是JWT
json web tokenJWT是一个开放标准rfc7519它定义了一种紧凑的、自包含的方式用于在各方之间以JSON对象安全地传输信息。它是以JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
2.JWT能做什么
授权
这是使用JWT的最常见方案。一旦用户登录每个后续请求将包括JWT从而允许用户访问该令牌允许的路由服务和资源。单点登录是当今广泛使用JWT的一项功能因为它的开销很小并且可以在不同的域中轻松使用。
信息交换
JSON Web Token是在各方之间安全地传输信息的好方法。因为可以对JWT进行签名例如使用公钥/私钥对所以您可以确保发件人是他们所说的人。此外由于签名是使用标头和有效负载计算的因此您还可验证内容是否遭到篡改。 3.Session认证与JWT认证的区别
基于传统的Session认证策略#
a.认证方式#
http协议本身是一种无状态的协议而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证那么下一次请求时用户还要再一次进行用户认证才行。
因为根据http协议我们并不能知道是哪个用户发出的请求所以为了让我们的应用能识别是哪个用户发出的请求我们只能在服务器存储一份用户登录的信息这份登录信息sessionId会在响应时传递给浏览器告诉其保存为cookie,以便下次请求时发送给我们的应用这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。
b.Session认证的问题 每个用户经过我们的应用认证之后我们的应用都要在服务端做一次记录以方便用户下次请求的鉴别通常而言session都是保存在内存中而随着认证用户的增多服务端的开销会明显增大 用户认证之后服务端做认证记录如果认证的记录被保存在内存中的话这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源这样在分布式的应用上相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。 因为是基于cookie来进行用户识别的, cookie如果被截获用户就会很容易受到跨站请求伪造的攻击。 在前后端分离系统中就更加痛苦也就是说前后端分离在应用解耦后增加了部署的复杂性。 通常用户一次请求就要转发多次。如果用session 每次携带sessionid 到服务器服务器还要查询用户信息。同时如果用户很多。这些信息存储在服务器内存中给服务器增加负担。 CSRF跨站伪造请求攻击攻击session是基于cookie进行用户识别的, cookie如果被截获用户就会很容易受到跨站请求伪造的攻击。 sessionid 就是一个特征值表达的信息不够丰富。不容易扩展。而且如果你后端应用是多节点部署。那么就需要实现session共享机制。 不方便集群应用。
基于JWT认证的策略#
认证流程# 首先前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输https协议从而避免敏感信息被嗅探。 后端核对用户名和密码成功后将用户的id等其他信息作为JWT Payload负载将其与头部分别进行Base64编码拼接后签名形成一个JWT(Token)。形成的JWT就是一个形同xxx.yyy.zzz的字符串。 即token head.payload.singurater 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上退出登录时前端删除保存的JWT即可。 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题) HEADER 后端检查是否存在如存在验证JWT的有效性。例如检查签名是否正确检查Token是否过期检查Token的接收方是否是自己可选。 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作返回相应结果。
4.jwt优势# 简洁(Compact): 可以通过URLPOST参数或者在HTTP header发送因为数据量小传输速度也很快 自包含(Self-contained)负载中包含了所有用户所需要的信息避免了多次查询数据库 因为Token是以JSON加密的形式保存在客户端的所以JWT是跨语言的原则上任何web形式都支持。 不需要在服务端保存会话信息特别适用于分布式微服务。1.登录成功----返回json数据
5.JWT的结构#
令牌组成# 1.标头(Header) 2.有效载荷(Payload) 3.签名(Signature) 因此JWT通常如下所示:xxxxx.yyyyy.zzzzz 也就是 Header.Payload.Signature Header的组成信息# 标头通常由两部分组成令牌的类型即JWT和所使用的签名算法例如HMAC SHA256或RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。 注意:Base64是一种编码也就是说它是可以被翻译回原来的样子来的。它并不是一种加密过程。
// header的组成信息
{alg: HS256,typ: JWT
}Payload组成信息#
令牌的第二部分是有效负载其中包含声明。声明是有关实体通常是用户信息和其他数据的声明。同样的它会使用 Base64 编码组成 JWT 结构的第二部分
// payload组成信息
{id: 823,name: Code Duck,role: admin
}Signature的组成信息#
header和payload都是使用 Base64 进行编码的即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及我们自己的一个密钥然后使用 header 中指定的签名算法HS256进行签名。签名的作用是保证 JWT 没有被篡改过。 例如HMACSHA256( base64UrlEncode(header) . base64UrlEncode(payload), secret ); # 签名目的
- 最后一步签名的过程实际上是对头部以及负载内容进行签名防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改再进行编码最后加上之前的签名组合形成新的JWT的话那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名在不知道服务器加密时用的密钥的话得出来的签名也是不一样的。# 信息安全问题
- 在这里大家一定会问一个问题Base64是一种编码是可逆的那么我的信息不就被暴露了吗- 是的。所以在JWT中不应该在负载里面加入任何敏感的数据。在上面的例子中我们传输的是用户的User ID。这个值实际上不是什么敏感内容一般情况下被知道也是安全的。但是像密码这样的内容就不能被放在JWT中了。如果将用户的密码放在了JWT中那么怀有恶意的第三方通过Base64解码就能很快地知道你的密码了。因此JWT适合用于向Web应用传递一些非敏感信息。JWT还经常用于设计用户认证和授权系统甚至实现Web应用的单点登录。以上三部分进行整合#
JWT的真实面目
(Header)eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
(payLoad)eyJyb2xlIjoiZW1wbG95ZWUiLCJpZCI6IjQiLCJleHAiOjE1OTcxMjc3NjUsInVzZXJuYW1lIjoiamFzb24ifQ.
(Signature)WxIiTf7V4UaboMONu0UpPu-uQSuDQFZqepKKxLstnaU 1.登录成功---返回json数据 2.登录失败----返回json数据
3.权限不足----返回json数据