- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Springboot+Vue+shiro实现前后端分离、权限控制的示例代码由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本文总结自实习中对项目的重构。原先项目采用Springboot+freemarker模版,开发过程中觉得前端逻辑写的实在恶心,后端Controller层还必须返回Freemarker模版的ModelAndView,逐渐有了前后端分离的想法,由于之前,没有接触过,主要参考的还是网上的一些博客教程等,初步完成了前后端分离,在此记录以备查阅.
1、前后端分离思想 。
前端从后端剥离,形成一个前端工程,前端只利用Json来和后端进行交互,后端不返回页面,只返回Json数据。前后端之间完全通过public API约定.
2、后端 Springboot 。
Springboot就不再赘述了,Controller层返回Json数据.
1
2
3
4
5
6
7
8
9
10
11
|
@RequestMapping
(value =
"/add"
, method = RequestMethod.POST)
@ResponseBody
public
JSONResult addClient(
@RequestBody
String param) {
JSONObject jsonObject = JSON.parseObject(param);
String task = jsonObject.getString(
"task"
);
List<Object> list = jsonObject.getJSONArray(
"attributes"
);
List<String> attrList =
new
LinkedList(list);
Client client = JSON.parseObject(jsonObject.getJSONObject(
"client"
).toJSONString(),
new
TypeReference<Client>(){});
clientService.addClient(client, task, attrList);
return
JSONResult.ok();
}
|
Post请求使用@RequestBody参数接收.
3、前端 Vue + ElementUI + Vue router + Vuex + axios + webpack 。
这里主要说一下开发工程中遇到的问题:
1.跨域 。
由于开发中前端工程使用webpack启了一个服务,所以前后端并不在一个端口下,必然涉及到跨域:
XMLHttpRequest会遵守同源策略(same-origin policy). 也即脚本只能访问相同协议/相同主机名/相同端口的资源, 如果要突破这个限制, 那就是所谓的跨域, 此时需要遵守CORS(Cross-Origin Resource Sharing)机制。 解决跨域分两种:
1、server端是自己开发的,这样可以在在后端增加一个拦截器 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Component
public
class
CommonIntercepter
implements
HandlerInterceptor {
private
final
Logger logger = LoggerFactory.getLogger(
this
.getClass());
@Override
public
boolean
preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws
Exception {
//允许跨域,不能放在postHandle内
response.setHeader(
"Access-Control-Allow-Origin"
,
"*"
);
if
(request.getMethod().equals(
"OPTIONS"
)) {
response.addHeader(
"Access-Control-Allow-Methods"
,
"GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,PATCH"
);
response.addHeader(
"Access-Control-Allow-Headers"
,
"Content-Type, Accept, Authorization"
);
}
return
true
;
}
}
response.setHeader(
"Access-Control-Allow-Origin"
,
"*"
);
|
主要就是在Response Header中增加 "Access-Control-Allow-Origin: *" 。
1
2
3
4
|
if
(request.getMethod().equals(
"OPTIONS"
)) {
response.addHeader(
"Access-Control-Allow-Methods"
,
"GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,PATCH"
);
response.addHeader(
"Access-Control-Allow-Headers"
,
"Content-Type, Accept, Authorization"
);
}
|
由于我们在前后端分离中集成了shiro,因此需要在headers中自定义一个'Authorization'字段,此时普通的GET、POST等请求会变成preflighted request,即在GET、POST请求之前会预先发一个OPTIONS请求,这个后面再说.
2、server端不是自己开发的,可以在前端加proxyTable.
不过这个只能在开发的时候用,后续部署,可以把前端项目作为静态资源放到后端,这样就不存在跨域(由于项目需要,我现在是这么做的,根据网上博客介绍,可以使用nginx,具体怎么做可以在网上搜一下).
遇到了网上很多人说的,proxyTable无论如何修改,都没效果的现象.
1、(非常重要)确保proxyTable配置的地址能访问,因为如果不能访问,在浏览器F12调试的时候看到的依然会是提示404.
并且注意,在F12看到的js提示错误的域名,是js写的那个域名,并不是代理后的域名。(l楼主就遇到这个问题,后端地址缺少了查询参数,代理设置为后端地址,然而F12看到的错误依然还是本地的域名,并不是代理后的域名) 。
2、就是要手动再执行一次npm run dev 。
4、前后端分离项目中集成shiro 。
这里说一下实际开发集成过程中遇到的问题:
1、OPTIONS请求不带'Authorization'请求头字段:
前后端分离项目中,由于跨域,会导致复杂请求,即会发送preflighted request,这样会导致在GET/POST等请求之前会先发一个OPTIONS请求,但OPTIONS请求并不带shiro的'Authorization'字段(shiro的Session),即OPTIONS请求不能通过shiro验证,会返回未认证的信息.
解决方法:给shiro增加一个过滤器,过滤OPTIONS请求 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public
class
CORSAuthenticationFilter
extends
FormAuthenticationFilter {
private
static
final
Logger logger = LoggerFactory.getLogger(CORSAuthenticationFilter.
class
);
public
CORSAuthenticationFilter() {
super
();
}
@Override
public
boolean
isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
//Always return true if the request's method is OPTIONSif (request instanceof HttpServletRequest) {
if
(((HttpServletRequest) request).getMethod().toUpperCase().equals(
"OPTIONS"
)) {
return
true
;
}
}
return
super
.isAccessAllowed(request, response, mappedValue);
}
@Override
protected
boolean
onAccessDenied(ServletRequest request, ServletResponse response)
throws
Exception {
HttpServletResponse res = (HttpServletResponse)response;
res.setHeader(
"Access-Control-Allow-Origin"
,
"*"
);
res.setStatus(HttpServletResponse.SC_OK);
res.setCharacterEncoding(
"UTF-8"
);
PrintWriter writer = res.getWriter();
Map<String, Object> map=
new
HashMap<>();
map.put(
"code"
,
702
);
map.put(
"msg"
,
"未登录"
);
writer.write(JSON.toJSONString(map));
writer.close();
return
false
;
}
}
|
贴一下我的config文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
@Configuration
public
class
ShiroConfig {
@Bean
public
Realm realm() {
return
new
DDRealm();
}
@Bean
public
CacheManager cacheManager() {
return
new
MemoryConstrainedCacheManager();
}
/**
* cookie对象;
* rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,cookie的有效时间等等。
* @return
*/
@Bean
public
SimpleCookie rememberMeCookie(){
//System.out.println("ShiroConfiguration.rememberMeCookie()");
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie =
new
SimpleCookie(
"rememberMe"
);
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(
259200
);
return
simpleCookie;
}
/**
* cookie管理对象;
* rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
* @return
*/
@Bean
public
CookieRememberMeManager rememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
CookieRememberMeManager cookieRememberMeManager =
new
CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode(
"2AvVhdsgUs0FSA3SDFAdag=="
));
return
cookieRememberMeManager;
}
@Bean
public
SecurityManager securityManager() {
DefaultWebSecurityManager sm =
new
DefaultWebSecurityManager();
sm.setRealm(realm());
sm.setCacheManager(cacheManager());
//注入记住我管理器
sm.setRememberMeManager(rememberMeManager());
//注入自定义sessionManager
sm.setSessionManager(sessionManager());
return
sm;
}
//自定义sessionManager
@Bean
public
SessionManager sessionManager() {
return
new
CustomSessionManager();
}
public
CORSAuthenticationFilter corsAuthenticationFilter(){
return
new
CORSAuthenticationFilter();
}
@Bean
(name =
"shiroFilter"
)
public
ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter =
new
ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//SecurityUtils.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap =
new
LinkedHashMap<>();
//配置不会被拦截的链接,顺序判断
filterChainDefinitionMap.put(
"/"
,
"anon"
);
filterChainDefinitionMap.put(
"/static/js/**"
,
"anon"
);
filterChainDefinitionMap.put(
"/static/css/**"
,
"anon"
);
filterChainDefinitionMap.put(
"/static/fonts/**"
,
"anon"
);
filterChainDefinitionMap.put(
"/login/**"
,
"anon"
);
filterChainDefinitionMap.put(
"/corp/call_back/receive"
,
"anon"
);
//authc:所有url必须通过认证才能访问,anon:所有url都可以匿名访问
filterChainDefinitionMap.put(
"/**"
,
"corsAuthenticationFilter"
);
shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
//自定义过滤器
Map<String, Filter> filterMap =
new
LinkedHashMap<>();
filterMap.put(
"corsAuthenticationFilter"
, corsAuthenticationFilter());
shiroFilter.setFilters(filterMap);
return
shiroFilter;
}
/**
* Shiro生命周期处理器 *
@return
*/
@Bean
public
LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return
new
LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return
*/
@Bean
@DependsOn
({
"lifecycleBeanPostProcessor"
})
public
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator =
new
DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(
true
);
return
advisorAutoProxyCreator;
}
@Bean
public
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new
AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return
authorizationAttributeSourceAdvisor;
}
}
|
2、设置session失效时间 。
shiro session默认失效时间是30min,我们在自定义的sessionManager的构造函数中设置失效时间为其他值 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public
class
CustomSessionManager
extends
DefaultWebSessionManager {
private
static
final
Logger logger = LoggerFactory.getLogger(CustomSessionManager.
class
);
private
static
final
String AUTHORIZATION =
"Authorization"
;
private
static
final
String REFERENCED_SESSION_ID_SOURCE =
"Stateless request"
;
public
CustomSessionManager() {
super
();
setGlobalSessionTimeout(DEFAULT_GLOBAL_SESSION_TIMEOUT *
48
);
}
@Override
protected
Serializable getSessionId(ServletRequest request, ServletResponse response) {
String sessionId = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
//如果请求头中有 Authorization 则其值为sessionId
if
(!StringUtils.isEmpty(sessionId)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return
sessionId;
}
else
{
//否则按默认规则从cookie取sessionId
return
super
.getSessionId(request, response);
}
}
}
|
5、部署项目 。
前端项目部署主要分两种方法:
1、将前端项目打包(npm run build)成静态资源文件,放入后端,一起打包。后端写一个Controller返回前端界面(我使用Vue开发的是单页面应用),但是这样其实又将前后端耦合在一起了,不过起码做到前后端分离开发,方便开发的目的已经达成,也初步达成了要求,由于项目的需要,我是这样做的,并且免去了跨域问题.
1
2
3
4
|
@RequestMapping
(value = {
"/"
,
"/index"
}, method = RequestMethod.GET)
public
String index() {
return
"/index"
;
}
|
2.将前端工程另启一个服务(tomcat,nginx,nodejs),这样有跨域的问题.
说一下我遇到的问题:
1、nginx反向代理,导致当访问无权限的页面时,shiro 302到unauth的controller,访问的地址是https,重定向地址是http,导致了无法访问.
不使用shiro的 shiroFilter.setLoginUrl("/unauth"),
当页面无权限访问时,我们在过滤器里直接返回错误信息,不利用shiro自带的跳转。看过滤器中的onAccessDenied函数 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public
class
CORSAuthenticationFilter
extends
FormAuthenticationFilter {
private
static
final
Logger logger = LoggerFactory.getLogger(CORSAuthenticationFilter.
class
);
public
CORSAuthenticationFilter() {
super
();
}
@Override
public
boolean
isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
//Always return true if the request's method is OPTIONS
if
(request
instanceof
HttpServletRequest) {
if
(((HttpServletRequest) request).getMethod().toUpperCase().equals(
"OPTIONS"
)) {
return
true
;
}
}
return
super
.isAccessAllowed(request, response, mappedValue);
}
@Override
protected
boolean
onAccessDenied(ServletRequest request, ServletResponse response)
throws
Exception {
HttpServletResponse res = (HttpServletResponse)response;
res.setHeader(
"Access-Control-Allow-Origin"
,
"*"
);
res.setStatus(HttpServletResponse.SC_OK);
res.setCharacterEncoding(
"UTF-8"
);
PrintWriter writer = res.getWriter();
Map<String, Object> map=
new
HashMap<>();
map.put(
"code"
,
702
);
map.put(
"msg"
,
"未登录"
);
writer.write(JSON.toJSONString(map));
writer.close();
return
false
;
}
}
|
先记录这么多,有不对的地方,欢迎指出! 。
到此这篇关于Springboot+Vue+shiro实现前后端分离、权限控制的示例代码的文章就介绍到这了,更多相关Springboot+Vue前后端分离内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://blog.51cto.com/14570694/2510241 。
最后此篇关于Springboot+Vue+shiro实现前后端分离、权限控制的示例代码的文章就讲到这里了,如果你想了解更多关于Springboot+Vue+shiro实现前后端分离、权限控制的示例代码的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有一个使用 css 列的下拉菜单,当我使用 jquery slide() 时,它会调整下拉框的大小,并重排内容直到达到完整高度。 这是一个工作示例 https://codepen.io/peterg
我有一个带有嵌套 ScrollViewer 的 Expander,如下所示: 代码(简化版)
我想在所有 ajax 调用之后调用一些 javascript 函数。我知道如何调用每个单独的 ajax 调用中的函数,如下所示: function xyz() { if (window.XMLHttp
我想将值从应用程序端传递到 api。在此 api 调用中传递图像、名字、电子邮件、电话和位置。在 Debug模式下,检查值不会被传递。 代码下方 File file = null;
我正在尝试在使用reportlab生成的pdf中的表格后插入分页符,我正在使用以下函数生成pdf: def render_to_pdf(template_src, context_dict): t
CBPeripheralManager 是否有推荐的方法来终止连接。到目前为止我发现的最好的方法就是干脆不响应动态值,然后 BLE 堆栈似乎关闭了连接,但这似乎很粗糙。 一定有更好的方法吗? 最佳答案
我的 API 的 REST 端位于以下地址:http://test.jll.aplikacje-dedykowane.pl/rest/warehouse/all 。现在,我尝试返回在此页面准备的 JS
我有以下 CPP 代码。我想做的是,当我的 native 端发生错误时,我会通知 Java 该错误。我用了How can I catch SIGSEGV (segmentation fault) an
限制对象的方法之一是像这样给函数添加限制 def ten_objects(): obj = Model.objects.all()[0:10] # limit to 10 retur
我目前在电信公司实习,这是一个专业,也是本科生。我有很多选择。据我所知,我知道独立应用程序端的 c、c++、c#、java 语言,在移动端我尝试进入 android 世界,也知道 php、mysql、
我想让我的边框底部看起来像这样: 有一个 flex 的末端。目前它看起来像这样: 我尝试使用的 CSS 是 border-bottom-right-radius: 10px;。代码如下所示:
我有一个 Flutter 项目,突然间,据我所知,我没有做任何特别的事情..Android 端开始显示错误,我完全迷路了,我完全不知道哪里出了问题,也不知道为什么会这样。 这就是我打开 android
我有一个自定义对象列表 (List) 。我需要将此数据发送到 React Native 端以显示在平面列表中。我该怎么做?这个列表出现在 类 NativeToReact(reactContext:Re
我有这个代码: #if defined(NOT_STANDALONE) JNIEXPORT void JNICALL sumTraces (JNIEnv* env, jclass caller,
我有一个定义一对多模型关系的 Django 应用程序。模型如下所示: from django.db import models # Create your models here. class Str
我有以下代码,它根据 IFrame 内容的大小调整 IFrame 的大小: function setIframeHeight(id) {
如何创建自定义过滤器 angularjs javascript Controller 端?我想通过 SegmentId 在名为段的数组中搜索,以创建过滤器,该过滤器通过 SegmentId 在段数组搜
我的代码在 netbeans 8.0.2 中我几乎尝试了所有方法,但没有结果。请帮助我。如何在 netbeans 中显示它? 最佳答案 您只需单击源包(源文件),它就会显示您的项目文件。 关于java
我想这是纯 C++ 问题和 OpenGL 问题之间的一种交叉。我有一个统一的缓冲区,并在其中分配 sizeof(ShaderData) 字节的空间。我在着色器的 GPU 端使用 std140 布局。
我对 Hadoop 中 reduce 端的文件合并过程的理解有问题,因为它在“Hadoop:权威指南”(Tom White)中有所描述。引用它: When all the map outputs ha
我是一名优秀的程序员,十分优秀!