【SSM项目】尚筹网(五)项目改写:使用前后端分离的SpringSecurityJWT认证
在项目中加入SpringSecurity
1 加入依赖
<!-- SpringSecurity -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security-version}</version>
</dependency>
2 配置DelegatingFilterProxy代理过滤器
SpringSecurity需要借助一系列的ServletFilter来实现安全性的功能,但是肯定不能我们需要自己去一个个创建这些过滤器,这里使用了一种代理模式
,即创建一个DelegatingFilterProxy的Bean,在工作中会由这个Bean拦截发往应用的所有请求,并将请求委托给id为springSecurityFilterChain的bean进行SpringSecurity安全性处理。
2.1 使用web.xml的方式
这里最重要的是<fileter-name>必须设置为
springSecurityFilterChain
,因为我们将SpringSecurity配置在Web安全性之中会有一个名为springSecurityFilterChain的FilterBean,DelegatingFilterProxy就会将过滤逻辑委托给它进行创建。
2.2 使用Java的方式
这里我们是基于Servlet3.0搭建的项目,所以选用的Java的方式配置DelegatingFilterProxy,我们只需要创建一个继承AbstractSecurityWebApplicationInitializer
的实现类即可,这个实现类由于实现了WebApplicationInitializer
接口,因此会和我们的web容器一样会在启动的时候被Spring发现,然后会自动将配置好的DelegatingFilterProxy
注入Web容器。
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
}
3 创建SpringSecurity配置类
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
}
4 ☆谁来扫描我们配置的SpringSecurity配置类?
4.1 基于xml的方式
出现这个问题是由于我们当前的项目是有两个IOC容器的,一个是在AppConfig中配置的SpringMVC(扫描controller)另一个是在RootConfig中配置的Spring容器(扫描除了Controller的组件,如整合Mybatis,持久化等方面的组件)。而SpringSecurity是WEB层面的安全检查,主要应针对发送给应用的请求所以应该由MVC容器进行扫描。
但是衍生出来的问题就是SpringSecurity配置的DelegatingFilterProxy
会在被扫描的IOC容器中寻找之前提到的springSecurityFilterChain
的Bean,这样就会出现下面的问题:
问题分析:在web容器启动的源码中,默认就是去找父容器Spring容器
![](file:///E:/File/myNote/Java/尚筹网/笔记/新/权限管理/16.png?msec=1665015877589)
解决方法1:不使用ContextLoaderListener
,让DispatcherServlet加载所有的IOC容器。
DelegatingFilterProxy在初始化的时候查找IOC容器,找不到 放弃
第一次请求的时候再查找,找到SpringMVC的IOC容器,能够找到所需要的bean
但是这种方法会破坏原有程序的结构:ContextLoaderListener和DispatcherServlet各创建一个IOC容器。
解决方法2:修改源码(算了算了
4.2 基于Java的配置方式
基于Servlet3.0的方式搭建的项目则不会出现这个问题,原因是我们使用的是AbstractAnnotationConfigDispatcherServletInitializer
代替的web.xml并且这个实现类会自动创建DispatcherServlet
,随后通过@Configuration配置类的配置最后会合成到一个,都属于上面的DispatcherServlet创建的IOC容器,所以无论被那个配置类扫描到都能被所有的配置类所使用。
5 SpringSecurity工作原理
在初始化或者第一次请求时准备好过滤器链,具体任务由具体过滤器来实现。
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextPersistenceFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.www.BasicAuthenticationFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.session.SessionManagementFilter
org.springframework.security.web.access.ExceptionTranslationFilter
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
前后端分离的登录验证
1 放行登录和静态请求
这里我们使用的前后端分离的做法,这时候前端登录会显示403没有权限,所以只需要在SpringSecurity中放行登录请求就可以了。
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
// 登录白名单
private final String[] URL_WHITELIST = new String[] {
"/login",
"/logout",
"/test/**"
};
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.cors() // 开启跨域
.and()
.csrf() // 关闭csrf
.disable()
.formLogin()
//.successHandler()
//.failureHandler()
.and()
.logout()
//.logoutSuccessHandler()
.and() // 禁用session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
.and()
.authorizeRequests()
.antMatchers(URL_WHITELIST) // 登录白名单 放行
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
// 异常处理配置
// 自定义配置
}
}
这里注意要禁用CSRF,否则提交的表单必须携带之前提到的_csrf.parameterName,如下,否则仍然会报403错误
<form action="${pageContext.request.contextPath}/login" method="post">
<%-- <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>--%>
<div class="layadmin-user-login-main">
2 重写并配置登录成功和失败处理器
/**
* 登录成功处理器
*/
@Component
public class LoginSuccessHandle implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// 设置响应体格式为json
response.setContentType("application/json;application/json;charset=UTF-8");
String userName = "userName";
String token = JWTUtil.getJWTToken(userName);
// 将token封装为返回体
Map<String, String> map = new HashMap<>();
map.put("token", token);
R<Object> r = R.successWithData(map);
// 转换为json
Gson gson = new Gson();
String json = gson.toJson(r);
response.getWriter().write(json);
}
}
/**
* 登录失败处理器
*/
@Component
@Slf4j
public class LoginFailureHandle implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = e.getMessage();
if(e instanceof BadCredentialsException) {
message = "用户名或者密码错误捏";
}
R<Object> r = R.failed(message);
Gson gson = new Gson();
String json = gson.toJson(r);
response.getWriter().write(json);
}
}
在SpringSecurity中配置登录成功和失败处理器
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.cors() // 开启跨域
.and()
.csrf() // 关闭csrf
.disable()
.formLogin() // 表单登录
.usernameParameter("loginName")
.passwordParameter("passWord")
.successHandler(loginSuccessHandle)
.failureHandler(loginFailureHandle)
//.and()
//.logout()
//.logoutSuccessHandler()
.and() // 禁用session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
.and()
.authorizeRequests()
.antMatchers(URL_WHITELIST) // 登录白名单 放行
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
// 异常处理配置
// 自定义配置
}
paramter用于设置参数,不设置的话默认传来的数据为username 和 password
3 一个比较坑的点:SpringSecurity的默认登录验证不支持json问题
当登录接口为下面的情况时,前端是以json向后端发送登录数据,然后会经过登陆失败处理器返回登录失败:
export function login(data) {
return request({
url: '/login',
method: 'post',
data
})
}
当修改为get请求时,便能够正常登录由登录成功处理器处理响应了。
export function login(data) {
return request({
url: '/login',
method: 'post',
params: data
})
}
axios中post请求使用params参数会等同于get请求,如上面的请求接口就是
[http://localhost:9528/api/login?loginName=admin&passWord=111111]
当然也可以直接以?加参数的形式以get请求发送,效果是一样的
这种方式存在的问题是登录名和密码会直接显示在url里面,隐蔽性不好(隐蔽性主要是指的可以在浏览记录中检索,post请求携带的数据只是不会被检索到而已,至于安全性整个http都是不安全的)
使用json方式进行登录验证可以参考知乎的一篇文章这里先不写了,等后面有时间改进一下:[https://zhuanlan.zhihu.com/p/365515428](Spring Security 使用自带的 formLogin)
4 基于SpringSecurity的登录查库
上面的例子是在内存中进行登录验证的,接下来实现数据库验证登录信息。
4.1 编写UserDetailsService实现类
@Slf4j
@Component
public class MyUserDetailService implements UserDetailsService {
@Autowired
AdminService adminService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 数据库查询
Admin admin = adminService.getByLoginName(s);
if(admin == null) {
throw new UsernameNotFoundException("");
}
if("1".equals(admin.getStatus())) {
throw new AdminCountLockException("账号被禁用,请联系管理员解封");
}
String passWord = admin.getPassWord();
String userName = admin.getUserName();
return new User(userName, passWord, getAdminAuthority());
}
private List<GrantedAuthority> getAdminAuthority() {
return new ArrayList<>();
}
}
4.2 开启SpringSecurity数据库认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 内存验证
//auth.inMemoryAuthentication()
// .withUser("admin")
// .password("111111")
// .roles("admin");
auth.userDetailsService(userDetailsService);
}
4.3 开启带盐值加密的BCryptPasswordEncoder
@Configuration
@Import(BCryptPasswordEncoder.class)
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginSuccessHandle loginSuccessHandle;
@Autowired
private LoginFailureHandle loginFailureHandle;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
// 登录白名单
private final String[] URL_WHITELIST = new String[] {
"/login",
"/logout",
"/test/**"
};
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 内存验证
//auth.inMemoryAuthentication()
// .withUser("admin")
// .password("111111")
// .roles("admin");
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
5 实现JWT认证过滤器
目的:实现登录之后在不使用session的情况下对登录状态进行检验。
5.1 创建一个JWT自定义配置类
JWTAuthorityFilter
@Slf4j
public class JWTAuthorityFilter extends BasicAuthenticationFilter {
@Autowired
AdminService adminService;
@Autowired
MyUserDetailService userDetailService;
// 验证白名单
private final String[] URL_WHITELIST = new String[] {
"/login",
"/logout",
"/test/**"
};
public JWTAuthorityFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = request.getHeader("token");
String uri = request.getRequestURI();
log.info("请求URI:" + uri);
// 放行白名单
if(Arrays.asList(URL_WHITELIST).contains(uri)) {
chain.doFilter(request, response);
}
// token验证
CheckResult checkResult = JWTUtil.validateJwt(token);
if(!checkResult.isSuccess()) {
int errorCode = checkResult.getErrorCode();
if(JWTConstant.JWT_ERROR_CODE_NULL == errorCode) {
throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_NULL);
} else if(JWTConstant.JWT_ERROR_CODE_FAIL == errorCode) {
throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_FAIL);
} else {
throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_EXPIRE);
}
}
// 将用户认证信息放入SpringSecurity上下文
Claims claims = JWTUtil.parseJWT(token);
String userName = claims.getSubject();
Admin admin = adminService.getByLoginName(userName);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
= new UsernamePasswordAuthenticationToken(userName, null, userDetailService.getAdminAuthority());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
chain.doFilter(request, response);
}
}
过滤器的主要工作有:
获取请求头中的token进行验证,验证不通过则抛出JwtException。
放行验证白名单给后面的Filter
将用户认证信息放入SpringSecurity上下文
5.2 在SpringSecurity配置类中设置自定义配置过滤器
@Bean
JWTAuthorityFilter jwtAuthorityFilter() throws Exception {
JWTAuthorityFilter jwtAuthorityFilter = new JWTAuthorityFilter(authenticationManager());
return jwtAuthorityFilter;
}
.and()
.addFilter(jwtAuthorityFilter());
5.3 配置JWT异常处理
① 编写AuthenticationEntryPoint
实现类
/**
* JWT自定义认证失败处理
*/
@Slf4j
@Component
public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = e.getMessage();
log.error(message);
R<Object> r = R.failed(message);
Gson gson = new Gson();
String json = gson.toJson(r);
response.getWriter().write(json);
}
}
② 在SpringSecurity中配置JWT异常处理
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.cors() // 开启跨域
.and()
.csrf() // 关闭csrf
.disable()
.formLogin()
.usernameParameter("loginName")
.passwordParameter("passWord")
.successHandler(loginSuccessHandle)
.failureHandler(loginFailureHandle)
//.and()
//.logout()
//.logoutSuccessHandler()
.and() // 禁用session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
.and()
.authorizeRequests()
.antMatchers(URL_WHITELIST) // 登录白名单 放行
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
// 异常JWT异常处理配置
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
// 自定义配置JWT认证过滤器
.and()
.addFilter(jwtAuthorityFilter());
}
5.4 代码总结 + JWT异常配置无法生效
SpringSecurity配置类
@Configuration
@Import(BCryptPasswordEncoder.class)
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginSuccessHandle loginSuccessHandle;
@Autowired
private LoginFailureHandle loginFailureHandle;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Bean
JWTAuthorityFilter jwtAuthorityFilter() throws Exception {
return new JWTAuthorityFilter(authenticationManager());
}
// 登录白名单
private final String[] URL_WHITELIST = new String[] {
"/login",
"/logout",
"/test/**"
};
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 内存验证
//auth.inMemoryAuthentication()
// .withUser("admin")
// .password("111111")
// .roles("admin");
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.cors() // 开启跨域
.and()
.csrf() // 关闭csrf
.disable()
.formLogin()
.usernameParameter("loginName")
.passwordParameter("passWord")
.successHandler(loginSuccessHandle)
.failureHandler(loginFailureHandle)
//.and()
//.logout()
//.logoutSuccessHandler()
.and() // 禁用session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
.and()
.authorizeRequests()
.antMatchers(URL_WHITELIST) // 登录白名单 放行
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
// 异常JWT异常处理配置
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
// 自定义配置JWT认证过滤器
.and()
.addFilter(jwtAuthorityFilter());
}
}
JWT认证过滤器
@Slf4j
public class JWTAuthorityFilter extends BasicAuthenticationFilter {
@Autowired
AdminService adminService;
@Autowired
MyUserDetailService userDetailService;
// 验证白名单
private final String[] URL_WHITELIST = new String[] {
"/login",
"/logout",
"/test/**"
};
public JWTAuthorityFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = request.getHeader("token");
String uri = request.getRequestURI();
log.info("请求URI:" + uri);
// 放行白名单
if(Arrays.asList(URL_WHITELIST).contains(uri)) {
chain.doFilter(request, response);
}
// token验证
CheckResult checkResult = JWTUtil.validateJwt(token);
if(!checkResult.isSuccess()) {
int errorCode = checkResult.getErrorCode();
log.error(errorCode+"");
String message = JWTConstant.JWT_ERROR_MESSAGE_EXPIRE;;
if(JWTConstant.JWT_ERROR_CODE_NULL == errorCode) {
message = JWTConstant.JWT_ERROR_MESSAGE_NULL;
//throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_NULL);
} else if(JWTConstant.JWT_ERROR_CODE_FAIL == errorCode) {
message = JWTConstant.JWT_ERROR_MESSAGE_FAIL;
//throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_FAIL);
} else {
message = JWTConstant.JWT_ERROR_MESSAGE_EXPIRE;
//throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_EXPIRE);
}
response.setContentType("application/json;charset=UTF-8");
R<Object> r = new R<>(errorCode, message, null);
Gson gson = new Gson();
String json = gson.toJson(r);
response.getWriter().write(json);
return ;
}
// 将用户认证信息放入SpringSecurity上下文
Claims claims = JWTUtil.parseJWT(token);
String userName = claims.getSubject();
Admin admin = adminService.getByLoginName(userName);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
= new UsernamePasswordAuthenticationToken(userName, null, userDetailService.getAdminAuthority());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
chain.doFilter(request, response);
}
}
JWT认证失败处理
/**
* JWT自定义认证失败处理
*/
@Slf4j
@Component
public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = e.getMessage();
log.error(message);
R<Object> r = R.failed(message);
Gson gson = new Gson();
String json = gson.toJson(r);
response.getWriter().write(json);
}
}
其实上面之前的异常处理代码是根本生效不了的,搞了一晚上,问题是出现在JWT认证过滤器这里:
if(!checkResult.isSuccess()) {
int errorCode = checkResult.getErrorCode();
log.error(errorCode+"");
String message = JWTConstant.JWT_ERROR_MESSAGE_EXPIRE;;
if(JWTConstant.JWT_ERROR_CODE_NULL == errorCode) {
message = JWTConstant.JWT_ERROR_MESSAGE_NULL;
//throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_NULL);
} else if(JWTConstant.JWT_ERROR_CODE_FAIL == errorCode) {
message = JWTConstant.JWT_ERROR_MESSAGE_FAIL;
//throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_FAIL);
} else {
message = JWTConstant.JWT_ERROR_MESSAGE_EXPIRE;
//throw new JwtException(JWTConstant.JWT_ERROR_MESSAGE_EXPIRE);
}
response.setContentType("application/json;charset=UTF-8");
R<Object> r = new R<>(errorCode, message, null);
Gson gson = new Gson();
String json = gson.toJson(r);
response.getWriter().write(json);
return ;
}
这里注释掉的内容就是之前的代码,问题就是
①每次认证失败就会直接抛出异常到前端而不是异常处理器,也就是过滤器链根本执行不完,也就不可能这种情况到达异常处理器。
②然后我又尝试使用一个拦截器拦截抛出的异常然后包装返回,但是也是生效不了(机制还是不清楚。。需要多去看书)
代码应该是没有错误,但是应该是因为我使用的是Servlet3.0配置的web容器,然后导入的JWT而不是视频的SpringBoot + 各种场景启动器
【SSM项目】尚筹网(五)项目改写:使用前后端分离的SpringSecurityJWT认证的更多相关文章
- 开源项目之ASP.NET Core + Vue.js 的前后端分离的通用后台管理系统框架
年前看了这个开源项目感觉很不错,这个小项目对于传统的.net 开发人员,想做技术提升是一个很不错的参考案例. 开源项目演示地址:https://dnczeus.codedefault.com/logi ...
- 【Docker】1、 前后端分离项目 下载启动运行
人人开源前后端分离项目下载与配置 文章目录 人人开源前后端分离项目下载与配置 前后端分离框架介绍 后端项目下载与配置 1.renren-fast后台项目介绍 2.开发环境搭建 3.下载后端renren ...
- 《Spring Boot 入门及前后端分离项目实践》系列介绍
课程计划 课程地址点这里 本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 个部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 项目实践开发 ...
- 《论vue在前后端分离项目中的实践之年终总结》
我是2014年的时候开始了解知道的vue,当时vue还不太成熟,想用但是又怕自己hold不住,况且那时候vue还没有成熟的(路由.验证.ui组件)插件,社区也是不温不火的,再说也没有合适的机遇让我去项 ...
- 七个开源的 Spring Boot 前后端分离项目,一定要收藏!
前后端分离已经在慢慢走进各公司的技术栈,根据松哥了解到的消息,不少公司都已经切换到这个技术栈上面了.即使贵司目前没有切换到这个技术栈上面,松哥也非常建议大家学习一下前后端分离开发,以免在公司干了两三年 ...
- 八个开源的 Spring Boot 前后端分离项目,一定要收藏!
八个开源的 Spring Boot 前后端分离项目 最近前后端分离已经在慢慢走进各公司的技术栈,不少公司都已经切换到这个技术栈上面了.即使贵司目前没有切换到这个技术栈上面,我们也非常建议大家学习一下前 ...
- SSM框架中的前后端分离
认识前后端分离 在传统的web应用开发中,大多数的程序员会将浏览器作为前后端的分界线.将浏览器中为用户进行页面展示的部分称之为前端,而将运行在服务器,为前端提供业务逻辑和数据准备的所有代码统称为后端. ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储模式
前言 1.@LearningCoding 小伙伴关于用Sqlsugar在mysql数据库上的研究成果: sqlsugarcore支持mysql等数据库,在DbContext里面只需要设置dbtype为 ...
- 前后端分离开发,基于SpringMVC符合Restful API风格Maven项目实战(附完整Demo)!
摘要: 本人在前辈<从MVC到前后端分离(REST-个人也认为是目前比较流行和比较好的方式)>一文的基础上,实现了一个基于Spring的符合REST风格的完整Demo,具有MVC分层结构并 ...
- Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储+服务+抽象接口模式
本文梯子 本文3.0版本文章 前言 零.完成图中的粉色部分 2019-08-30:关于仓储的相关话题 一.创建实体Model数据层 二.设计仓储接口与其实现类 三.设计服务接口与其实现类 四.创建 C ...
随机推荐
- vue-封装组件-结合vant实现点击按钮弹出泡泡(Popover)事件控制多个泡泡出现时,弹出对应的泡泡
<template> <div class="sale-share-box"> <span class="sale-share-btn&qu ...
- 3html5
<label>网址:</label><input type="url" name="" required><br> ...
- MySQL的卸载与安装
卸载 1.右键点击我的电脑 -->服务-->停掉MySQL的服务 2.控制面板卸载MySQL 3.删除隐藏文件夹 C:\ProgramData下的MySQL文件夹 4.删除MySQL文件夹 ...
- axios实现无感刷新
前言 最近在做需求的时候,涉及到登录token,产品提出一个问题:能不能让token过期时间长一点,我频繁的要去登录. 前端:后端,你能不能把token 过期时间设置的长一点. 后端:可以,但是那样做 ...
- sqlite bundle 的含义,和 sqlite.dll, SQLite.Interop.dll, System.Data.SQLite.dll 三者之间的关系
sqlite bundle 的含义,和 sqlite.dll, SQLite.Interop.dll, System.Data.SQLite.dll 三者之间的关系. bundle 表示不需要配合 S ...
- 初识MPC
MPC调研报告 这是一篇关于MPC的调研报告,主要介绍了我对MPC领域的一些基础认识.全文按照这样的方式组织:第一节我介绍了什么是MPC以及MPC的起源:第二节介绍了MPC领域常用的一些符号和安全 ...
- CentOS7带图形界面不能扫描网卡。
CentOS7在带有图形界面,在配置/sysconfig/network-script/ifcfg-ens*后不能识别到网卡,有可能是NetworkManager打开导致,可以使用systemctl ...
- 观察APP运行日志
一.Android采用log工具打印日志,他将各类日志分为五个等级 1.log.e:表示错误信息,比如可能导致程序崩溃的异常 2.log.w:表示警告信息 3.log.i:表示一般信息 4.log.d ...
- pandas 某几列转为json/dic 格式
#%% import pandas as pd df=pd.read_excel('工作表.xlsx') col_list=list(df.columns) del_col_list =['c','d ...
- Fiddler一些用法学习记录
最近项目中用Fiddler较多,只会之前掌握的一些最简单的用法显得有点不太够.记录一下学习到的新用法. 一.需要mock.打开AutoResponder,Add Rule,填上需要mock的网址,需要 ...