Marco's Java【Shiro进阶(一) 之 Shiro+SMM集成Maven项目串烧篇(下)】
作者:互联网
前言
Shiro+SMM集成Maven项目串烧篇的(上)(下)篇看完之后是不是有所收获呢?相信把整个流程理顺了,整明白了,那么再遇到类似的项目思绪就不会那么乱啦。那么如果觉得前面的内容已经掌握的差不多的朋友可以继续我们的旅程,本节的知识相当重要,可以说是为后面的分布式的学习打下基础。
SMM及Shiro集成之前后端分离
本节涉及到一个概念,叫前后端分离,那什么是前后端分离呢?
简单来说前后端分离是一种开发模式,从字面意思上来理解就是前端和后端的代码分离,意思就是前端做前端的事,后端做后端的事,再往深理解,一般前端会负责将数据按照产品设计渲染以及涉及调用后端数据的接口,从而实现产品功能,而后端只负责一件事,按照前端的接口数据的要求或者规范提供相应格式的数据!
前后端分离的开发模式的初衷为了让专业的人做专业的事,且现在前端和后端可以通过接口文档实现并行开发,变相的提高开发效率。
"各司其职"一直都是一种理念,不仅仅是Java类的分包,还是Maven的项目分配,我们的前后端分离,还有分布式架构等等,无非都是遵循着这种理念,目的都是为了让开发更快,更稳。
Shiro的权限注解
了解了前后端分离之后,大家应该能隐隐约约猜到我们现在需要改动什么… 没错,就是改动Controller,既然是前后端分离了嘛,那么我的"废铁段位"的前端页面就可以抛弃了!
我只用提供数据给你,你前端帮我渲染一下就好了,那么Controller中返回的数据格式就都应该是Json格式了,因此Controller上的注解我统一都会改成@RestController
这里还涉及到Shiro的权限注解的概念,也就是下面的两个示例注解
@RequiresPermissions("user:update")
@RequiresRoles("超级管理员")
这两个注解大家有没有感觉到一丝熟悉?没错,这就是我们们之前在jsp页面上写的代码,只不过使用的是注解的方式展现出来,原因是我现在使用的前后端分离,写不了jsp页面,因此,只能在后台做文章。当然,它的目的依然是为了确认这个用户有没有指定的权限,或者有没有指定的角色。
开启Shiro注解
根据以往的经验,我们使用注解的时候是需要开启注解使用权限的,因此需要在springmvc.xml中配置
<!-- 启动Shiro的注解开始 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"></bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
<!-- 启动Shiro的注解结束 -->
不知道大家会不会有疑问,为什么Shiro的配置不配在application-shiro里,而要配在看似不相干的springmvc.xml中呢?
其实,这也是shiro使用的一个"巨坑",因为当我们配置在application-shiro时,它会在applicationContext.xml中被import,而applicationContext.xml又会在web.xml中被导入,但是与此同时,springmvc.xml也被导入了
但是由于DispatcherServlet配置了<load-on-startup>1</load-on-startup>
,因此被初始化的优先级高于applicationContext.xml,换句话说会先于它被创建,那么这有什么问题呢?
我们反过来试想一下,如果我配置在applicationContext.xml,那么注解并不会在DispatcherServlet被初始化的时候生效,那么DispatcherServlet将我们的请求先放行之后,请求已经找到对应要执行的控制器中的方法并进去了,这个时候我们的注解再生效,那么请问我们配置这个拦截(比方说@RequiresPermissions("user:update")
) 又有何意义呢?
当然如果你一定要在applicationContext.xml中配置也不是完全不可以,但是注解的生效范围只能是service层,因为applicationContext.xml中只扫描到service为止,再上面就是springmvc.xml扫描的范畴了。
因此这一点一定一定要注意,理解不了没有关系,记住这样去配置就可以啦~
开启全局异常监控
这里又接触到了一个新的概念,所谓全局异常监控就是全局的监听你抛出来的异常,然后逮住它!返回一个友好的提示,而不是直接给一个500出去。
这里有有几个注解,大家先提前了解下
@ControllerAdvice :这个注解是监视Controller里面是否有异常发生,如果发生就跳转页面
@RestControllerAdvice : 这个注解是监视Controller里面是否有异常发生,返回json串
@ExceptionHandler(UnauthorizedException.class) :当发生UnauthorizedException类型异常就是回调当前注解所在的方法
package com.marco.aspect;
import java.util.HashMap;
import java.util.Map;
import org.apache.shiro.authz.UnauthenticatedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHanlderAdvisor {
/**
* 未授权的异常捕捉
* @return
*/
@ExceptionHandler(UnauthenticatedException.class)
public Object unauthorized() {
Map<String,Object> map = new HashMap<>();
map.put("msg", "未授权,请联系管理员");
map.put("code", -1);
return map;
}
}
接下来我们把所有的View层的前端页面全部删掉!对,你没有听错,是全部!
创建CommonController控制器处理未登录请求
package com.marco.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("common")
public class CommonController {
@RequestMapping("unLogin")
public Object unLogin() {
Map<String,Object> map=new HashMap<>();
map.put("code", -1);
map.put("msg", "未登陆");
return map;
}
}
创建完成之后,大家修改一下application-shiro.xml中的这个地方的配置
当用户没有登录的时候,shiroFilter会自动的帮我们请求/common/unLogin.action
,是不是很贴心~
修改LoginController
之前提到了,我们的LoginController返回给前端的只有Json数据,因此我们需要做一次调整
package com.marco.controller;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.marco.utils.ActiveUser;
@RestController
@RequestMapping("login")
public class LoginController {
@RequestMapping("toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("login")
public Map<String,Object> login(String username, String userpwd, HttpSession session) {
System.out.println(username + " " + userpwd);
//封装token
UsernamePasswordToken token = new UsernamePasswordToken(username, userpwd);
//获取Subject主体
Subject subject = SecurityUtils.getSubject();
// 调用主体的登陆方法
Map<String,Object> map = new HashMap<>();
Integer code = 1;
String msg = "";
try {
subject.login(token);
System.out.println("登陆成功");
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
session.setAttribute("user", activeUser.getUser());
msg = "登陆成功";
} catch (IncorrectCredentialsException e) {
code = -1;
msg = "密码不正确";
System.err.println("密码不正确");
} catch (UnknownAccountException e) {
code = -1;
msg = "用户名不存在";
System.err.println("用户名不存在");
}
map.put("code", code);
map.put(msg, msg);
return map;
}
}
测试
到此为止我们的项目已经改造完成,接下来我们测试一下,我们先不登陆,看看页面给我们返回什么
接着我们再登录,先输入错误的密码
再输入错误的用户名
接着输入正确的用户名和密码
接着访问我们有访问权限的功能,比如说 user:add
我们再访问没有权限的功能,比如说 user:export
什么鬼?我不是用GlobalExceptionHanlderAdvisor抓住异常了吗?怎么还是给我500?
原因呢是因为我们没有对aspect包进行扫描,这个对象没有被加载进IoC容器中,这是一个经常会出问题的点,之所以让这个异常抛出来,是为了能够加深大家的印象,很贴心有没有?
哈哈,不扯了,我们加上扫描再试试看
后语
到此为止呢,我们Shiro+SMM集成Maven项目串烧系列篇就结束啦,不知道大家看完有没有点收获呢?
虽然Shiro理解起来相对与其他的框架可能比较困难,有的朋友可能会觉得不好用,但是如果熟练掌握Shiro的用法,稍微钻研一下,对我们后期管理系统的设计帮助是相当大的。
标签:xml,map,Marco,Java,org,注解,import,Shiro 来源: https://blog.csdn.net/weixin_44698119/article/details/98242922