在之前的案例中使用Shiro完成授权的功能是通过Shiro的jstl表达式来对页面上的链接入口进行限制来完成的。
但是,在前后端分离的项目中,后端开发人员只负责开发后端接口,没有前台页面的支持。在此,我们可以使用Shirl的注解,来对Controller的Rest接口方法进行限定,通过不同的情况向页面返回不同的json数据。
一、创建ResultObj用于封装数据转换成json显示给前台
package com.xsh.pojo;
public class ResultObj {
private String msssage;
public ResultObj(String msssage) {
this.msssage = msssage;
}
public String getMsssage() {
return msssage;
}
public void setMsssage(String msssage) {
this.msssage = msssage;
}
}
二、解决用户未登录的访问问题
在之前,我们是在application-shiro.xml文件中配置了用户未登录时跳转到登录页面的相关配置。此时没有前端页面的支持,我们就需要创建Filter拦截未登录的请求并返回json提示。
package com.xsh.filter;
import com.alibaba.fastjson.JSON;
import com.xsh.pojo.ResultObj;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
public class ShiroLoginFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse rep= (HttpServletResponse) response;
rep.setCharacterEncoding("UTF-8");
rep.setContentType("application/json");
ResultObj resultObj=new ResultObj("用户未登录");
PrintWriter out = rep.getWriter();
out.write(JSON.toJSONString(resultObj));
out.flush();
out.close();
return false;
}
}
以上过滤器继承了Shiro的FormAuthenticationFilter ,并且重写了onAccessDenied()方法,该方法会在未登录的请求访问的时候就被回调,所以可以通过这个方法的内容给前台显示未登录信息。
三、解决用户登录成功或者失败的显示问题
package com.xsh.controller;
import com.xsh.pojo.ResultObj;
import com.xsh.utils.ActivierUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("/login")
public class LoginController {
@RequestMapping("/login")
public ResultObj login(String username, String password, HttpSession session){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken(username,password);
try{
subject.login(token);
ActivierUser activierUser = (ActivierUser) subject.getPrincipal();
session.setAttribute("user",activierUser.getUser());
return new ResultObj("登录成功");
}catch (Exception e){
return new ResultObj("登陆失败");
}
}
}
在此的解决方案非常简单,只是将返回的结果信息转换成了json数据。
四、完善相关控制器方法的权限限定
package com.xsh.controller;
import com.xsh.pojo.ResultObj;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequiresPermissions(value = "user:query")
@RequestMapping("/query")
public ResultObj query(){
return new ResultObj("用户查询");
}
@RequiresPermissions(value = "user:add")
@RequestMapping("/add")
public ResultObj add(){
return new ResultObj("用户添加");
}
@RequiresPermissions(value = "user:update")
@RequestMapping("/update")
public ResultObj update(){
return new ResultObj("用户修改");
}
@RequiresPermissions(value = "user:delete")
@RequestMapping("/delete")
public ResultObj delete(){
return new ResultObj("用户删除");
}
}
在该控制器中,通过@RequiresPermissions注解来对控制器方法进行了权限限定。
但是此时并不能生效,已知Shiro的验证是通过异常来进行结果的显示,也就是说当用户访问权限不够的时候Shiro会返回一个UnauthorizedException异常。
所以,在此我们需要开启Shiro的注解支持,并且拦截UnauthorizedException异常来返回提示信息
五、在springmvc.xml文件中添加Shiro的注解支持
<!--开启Shiro的注解支持-->
<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor" id="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>
六、创建拦截全局异常监听类监听UnauthorizedException异常
package com.xsh.handler;
import com.xsh.pojo.ResultObj;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常监控,当项目中发生异常就会触发其中的方法
*/
@RestControllerAdvice
public class GlobalExceptionHanderAdvise {
/**
* 监听Shiro的未授权异常,当用户访问权限不够的功能时就会触发该异常
* 该方法监听这个异常然后返回json提示信息
* @return
*/
@ExceptionHandler(value = {UnauthorizedException.class})
public ResultObj Unauthorized(){
return new ResultObj("权限不够");
}
}
此时,使用注解来完成用户权限验证就结束了。如果在springmvc.xml文件中扫描注解的包范围太小就必须要添加全局异常监听类的注解扫描,不然该类也无法发挥作用。