场景:因开发需要,时间有限,便在某云上下载了一个开源框架-脚手架,
希望能便捷开发,提高开发效率
ps:框架是个好框架,大家也可以学习学习
springboot_v2
问题描述:因为这个框架本身采用shiro 做权限拦截,本身是属于一个web
管理后台框架,但是我这边需求是还需要对接手机接口,
但是,在框架中,针对未登录用户的拦截是返回login.html,
手机接口也是采用同样拦截,导致手机接口在未登录的情况下
,会返回一个login.html,无法解析,但是对于web这个是必须需要的
,第一想法:卧槽,这么简单个玩意儿,容我2分钟搞定,
。。。。。。。。。。半个小时过后-未果,各种百度,必应,google,
头都看晕了,(我一看文档就犯困。。。。) shiro拦截顺序永远都在
WebMvcConfigurationSupport 前面·因为我已经踩了坑,
我在做拦截的时候
,拦截到的url全是shiro已经拦截,并且重定向到login的路径···
首先:先上代码 ShiroConfig
package com.fc.test.shiro.config;
import com.fc.test.shiro.service.ExtendRolesAuthorizationFilter;
import com.fc.test.shiro.util.ShiroUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fc.test.shiro.service.MyShiroRealm;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 权限配置文件
* @ClassName: ShiroConfiguration
* @author fuce
* @date 2018年8月25日
*
*/
@Configuration
public class ShiroConfig {
/**
* 这是shiro的大管家,相当于mybatis里的SqlSessionFactoryBean
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Map filtersMap = new LinkedHashMap();
// //自定义拦截器
// filtersMap.put("authc", new ShiroLoginFilter());
// shiroFilterFactoryBean.setFilters(filtersMap);
shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap());
//登录
shiroFilterFactoryBean.setLoginUrl("/admin/login");
//首页
shiroFilterFactoryBean.setSuccessUrl("/admin/login");
shiroFilterFactoryBean.setSuccessUrl("/admin/index");
// //错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
// //页面权限控制
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* web应用管理配置
* @param shiroRealm
* @param cacheManager
* @param manager
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager(Realm shiroRealm,CacheManager cacheManager,RememberMeManager manager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setCacheManager(cacheManager);
securityManager.setRememberMeManager(manager);//记住Cookie
securityManager.setRealm(shiroRealm);
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* session过期控制
* @return
* @author fuce
* @Date 2019年11月2日 下午12:49:49
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager defaultWebSessionManager=new DefaultWebSessionManager();
// 设置session过期时间3600s
Long timeout=60L*1000*60;//毫秒级别
defaultWebSessionManager.setGlobalSessionTimeout(timeout);
return defaultWebSessionManager;
}
/**
* 加密算法
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");//采用MD5 进行加密
hashedCredentialsMatcher.setHashIterations(1);//加密次数
return hashedCredentialsMatcher;
}
/**
* 记住我的配置
* @return
*/
@Bean
public RememberMeManager rememberMeManager() {
Cookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);//通过js脚本将无法读取到cookie信息
cookie.setMaxAge(60 * 60 * 24);//cookie保存一天
CookieRememberMeManager manager=new CookieRememberMeManager();
manager.setCookie(cookie);
return manager;
}
/**
* 缓存配置
* @return
*/
@Bean
public CacheManager cacheManager() {
MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存
return cacheManager;
}
/**
* 配置realm,用于认证和授权
* @param hashedCredentialsMatcher
* @return
*/
@Bean
public AuthorizingRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {
MyShiroRealm shiroRealm = new MyShiroRealm();
//校验密码用到的算法
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return shiroRealm;
}
/**
* 启用shiro方言,这样能在页面上使用shiro标签
* @return
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
/**
* 启用shiro注解
*加入注解的使用,不加入这个注解不生效
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
shiroFilterMap
package com.fc.test.shiro.config;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @ClassName: ShiroFilterMapFactory
* @author fuce
* @date 2018年8月26日
*
*/
public class ShiroFilterMapFactory {
/**
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
roles(角色):例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms(权限):例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
*/
public static Map<String, String> shiroFilterMap() {
// 设置路径映射,注意这里要用LinkedHashMap 保证有序
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//对所有用户认证
filterChainDefinitionMap.put("/user/loginByUser", "anon"); //用户登录
filterChainDefinitionMap.put("/user/loginByPhone", "anon");//手机号登录
filterChainDefinitionMap.put("/user/getPhoneCode", "anon");//获取手机验证码
filterChainDefinitionMap.put("/user/registered", "anon");//注册
filterChainDefinitionMap.put("/user/forgetPassword", "anon");//忘记密码
//swagger放开过滤
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/v2/**", "anon");
filterChainDefinitionMap.put("/swagger-resources/**", "anon");
filterChainDefinitionMap.put("/static/**", "anon");//静态文件
filterChainDefinitionMap.put("/admin/login", "anon");//登录页面
filterChainDefinitionMap.put("/admin/logout", "logout");//登出页面
//放验证码
filterChainDefinitionMap.put("/captcha/**", "anon");//验证码
// 释放 druid 监控画面
filterChainDefinitionMap.put("/druid/**", "anon");
//释放websocket请求
filterChainDefinitionMap.put("/websocket", "anon");
//前端
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/index", "anon");//任务调度暂时放开
filterChainDefinitionMap.put("/quartz/**", "anon");
//
//对所有页面进行认证
filterChainDefinitionMap.put("/**","authc");
return filterChainDefinitionMap;
}
}
可以很清楚的看到
shiroFilterFactoryBean.setLoginUrl("/admin/login");
shiroFilterFactoryBean.setSuccessUrl("/admin/login");
shiroFilterFactoryBean.setSuccessUrl("/admin/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
配置了验证不通过跳转页面,我们的login,请看代码
/**
* 请求到登陆界面
* @param request
* @return
*/
@ApiOperation(value="请求到登陆界面",notes="请求到登陆界面")
@GetMapping("/login")
public Object login(ModelMap modelMap ,ServletRequest request, ServletResponse response) throws IOException {
try {
if ((null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) || SecurityUtils.getSubject().isRemembered()) {
return "redirect:/"+prefix+"/index";
} else {
System.out.println("--进行登录验证..验证开始");
modelMap.put("RollVerification", V2Config.getRollVerification());
System.out.println("V2Config.getRollVerification()>>>"+V2Config.getRollVerification());
// return AjaxResult.NotLogin("login");
return "login";
}
} catch (Exception e) {
e.printStackTrace();
}
return "login";
}
未验证通过的请求被指向了这个接口中,项目中采用的thymeleaf模板
导致我手机未登录接口或者seesion过期导致跳转到这个接口,返回html,但是这明显不适用手机接口
如图:返回的html,当然对于web这个是必需品
百思不得其解之后,便开始决定自定义一个拦截器 基于WebMvcConfigurationSupport
前提是得把手机接口,和web接口分开(装在不同得controller)
开工
1:自定义一个异常
package com.fc.test.common.exception.demo;
/**
* 手机接口异常
* @ClassName: PhoneModeException
* @author lp
* @date 2019-12-24 15:45
*
*/
public class PhoneModeException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public PhoneModeException()
{
}
}
针对上面异常返回的状态码 --这个是重点
package com.fc.test.common.exception;
import javax.servlet.http.HttpServletRequest;
import com.fc.test.common.exception.demo.PhoneModeException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;
import com.fc.test.common.domain.AjaxResult;
import com.fc.test.common.exception.demo.DemoModeException;
import com.fc.test.util.ServletUtils;
/**
* 全局异常处理
* @author fuce
* @date: 2018年9月9日 下午10:52:55
*/
@RestControllerAdvice
public class GlobalExceptionResolver{
private static Logger logger = LoggerFactory.getLogger(GlobalExceptionResolver.class);
/**
* 权限校验失败 如果请求为ajax返回json,普通请求跳转页面
*/
@ExceptionHandler(AuthorizationException.class)
public Object handleAuthorizationException(HttpServletRequest request, AuthorizationException e)
{
//开发环境打印异常,正式环境请注销
logger.error(" 权限校验异常》》"+e.getMessage(), e);
if (ServletUtils.isAjaxRequest(request))
{
return AjaxResult.error(e.getMessage());
}
else
{
ModelAndView mv;
//shiro异常拦截
if(e instanceof UnauthorizedException){
//未授权异常
mv = new ModelAndView("/error/403");
return mv;
}else if(e instanceof UnauthenticatedException){
//未认证异常
mv = new ModelAndView("/error/403");
return mv;
}
else {
mv = new ModelAndView();
return mv;
}
}
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public AjaxResult validatedBindException(BindException e)
{
logger.error("自定义验证异常", e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return AjaxResult.error(message);
}
/**
* 请求方式不支持
*/
@ExceptionHandler({ HttpRequestMethodNotSupportedException.class })
public AjaxResult handleException(HttpRequestMethodNotSupportedException e)
{
logger.error("请求方式不支持异常:", e);
return AjaxResult.error("不支持' " + e.getMethod() + "'请求");
}
/**
* 手机接口拦截
*/
@ExceptionHandler(PhoneModeException.class)
public AjaxResult PhoneModeException(PhoneModeException e)
{
return AjaxResult.NotLogin("未登录,请重新登录");
}
}
2:拦截逻辑-web Controller与app Controller 区分
我们来复习一下我们要做什么:针对手机接口,未登录用户返回状态码,并不是html ok 继续····
package com.fc.test.common.interceptor;
import cn.hutool.core.util.StrUtil;
import com.fc.test.common.conf.V2Config;
import com.fc.test.common.exception.demo.DemoModeException;
import com.fc.test.common.exception.demo.PhoneModeException;
import com.fc.test.shiro.util.ShiroUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义拦截器
* @author lp
*
*/
public class PhoneInterceptor implements HandlerInterceptor {
/*************手机拦截器start*************/
/**需要被拦截的post请求url**/
public static List<String> posturllist=new ArrayList<String>();
/**需要被拦截的get请求url**/
public static List<String> geturllist=new ArrayList<String>();
/**
* 初始化静态块,避免多次创建调用
* 需要拦截的请求集合
*/
static {
//用户消费接口
posturllist.add("/api/CBuyDataController/add_phone");
geturllist.add("/api/CBuyDataController/add_phone");
posturllist.add("/api/CBuyDataController/listByUser_phone");
geturllist.add("/api/CBuyDataController/listByUser_phone");
//返回课程文件
posturllist.add("/api/CCourseContentsController/cCourseContentsId_Phone");
geturllist.add("/api/CCourseContentsController/cCourseContentsId_Phone");
//目录列表
posturllist.add("/api/CCourseContentsController/listCCourseContents_Phone");
geturllist.add("/api/CCourseContentsController/listCCourseContents_Phone");
//课程详情
posturllist.add("/api/CCourseController/CCourseData_Phone");
geturllist.add("/api/CCourseController/CCourseData_Phone");
//课程列表
posturllist.add("/api/CCourseController/listCCourse_Phone");
geturllist.add("/api/CCourseController/listCCourse_Phone");
//测评列表
posturllist.add("/api/CEvaluationController/listCEvaluation_Phone");
geturllist.add("/api/CEvaluationController/listCEvaluation_Phone");
//测评题目
posturllist.add("/api/CEvaluationController/topicList_Phone");
geturllist.add("/api/CEvaluationController/topicList_Phone");
//查询课程/文章/测评是否购买(场景:用户点开课程/文章/测评详情时候调用-未缴费,提示缴费)--手机
posturllist.add("/api/CommonController/CheckIsPay_Phone");
geturllist.add("/api/CommonController/CheckIsPay_Phone");
//查询课程文章是否需要购买
posturllist.add("/api/CommonController/CheckVip_Phone");
geturllist.add("/api/CommonController/CheckVip_Phone");
//字典详情接口
posturllist.add("/api/CommonController/DictList");
geturllist.add("/api/CommonController/DictList");
//咨询首页
posturllist.add("/api/CommonController/advisoryHome_Phone");
geturllist.add("/api/CommonController/advisoryHome_Phone");
//文章列表
posturllist.add("/api/SysArticleController/listSysArticle_Phone");
geturllist.add("/api/SysArticleController/listSysArticle_Phone");
//文章详情
posturllist.add("/api/SysArticleController/viewinfoPhone");
geturllist.add("/api/SysArticleController/viewinfoPhone");
//用户信息完善
posturllist.add("/api/user/dataGreate");
geturllist.add("/api/user/dataGreate");
//忘记密码
posturllist.add("/api/user/forgetPassword");
geturllist.add("/api/user/forgetPassword");
// posturllist.add("");
// geturllist.add("");
// posturllist.add("");
// geturllist.add("");
}
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)throws Exception {
// System.out.println(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
}
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)throws Exception {
// System.out.println(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
//String url2=request.getScheme()+"://"+ request.getServerName();
//System.out.println("xxxxxxxxx==="+"http://localhost:8081/");
//System.out.println(">>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");
//设置前端的全局 地址,如果前端网页错乱请修改这儿
request.setAttribute("rootPath", request.getContextPath());
Boolean b = ifurl(request, response);
if(b) {
throw new PhoneModeException();
}
return true;// 只有返回true才会继续向下执行,返回false取消当前请求
}
/**
* 所有接口都拦截未登录状态码
* @param request
* @param response
* @return
* @author lp
* @Date 2019年12月24日 下午5:17:30
*/
public Boolean ifurl(HttpServletRequest request, HttpServletResponse response) {
//当前请求
String requesturl=request.getRequestURI();
request.isRequestedSessionIdFromURL();
if(request.getMethod().equals("POST")) {
for (String postrul : posturllist) {
if(StrUtil.containsAnyIgnoreCase(requesturl, postrul)) {
if(ShiroUtils.getUser() ==null){
return true;
}else{
return false;
}
}
}
}else {
for (String geturl : geturllist) {
if(StrUtil.containsAnyIgnoreCase(requesturl, geturl)) {
if(ShiroUtils.getUser() ==null){
return true;
}else{
return false;
}
}
}
}
return false;
}
}
上面代码中,定义出了所有手机接口包括POST/GET请求,并且获取当前登录用户,为空?那就是我们想要的东西了
3创建WebMvcConfigurationSupport 过滤器
package com.fc.test.common.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurationSupport {
/** 解决跨域问题 **/
@Override
public void addCorsMappings(CorsRegistry registry){
/*
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOrigins("*")
// 是否允许证书
.allowCredentials(true)
// 设置允许的方法
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
super.addCorsMappings(registry);
*/
}
/** 添加拦截器 **/
@Override
protected void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new PhoneInterceptor());
super.addInterceptors(registry);
}
/** 这里配置视图解析器 **/
@Override
protected void configureViewResolvers(ViewResolverRegistry registry){
super.configureViewResolvers(registry);
}
/** 配置内容裁决的一些选项 **/
@Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer){
super.configureContentNegotiation(configurer);
}
/** 视图跳转控制器 **/
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
}
/** 静态资源处理 **/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
/** 默认静态资源处理器 **/
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//super.configureDefaultServletHandling(configurer);
//configurer.enable("stati");
configurer.enable();
}
}
其实只需要注意这段代码
/** 添加拦截器 **/
@Override
protected void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new PhoneInterceptor());
super.addInterceptors(registry);
}
我们把刚才我定义那个拦截url的方法添加到了拦截器
4:接下来我们取处理shiro拦截机制
package com.fc.test.shiro.config;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @ClassName: ShiroFilterMapFactory
* @author fuce
* @date 2018年8月26日
*
*/
public class ShiroFilterMapFactory {
/**
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
roles(角色):例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms(权限):例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
*/
public static Map<String, String> shiroFilterMap() {
// 设置路径映射,注意这里要用LinkedHashMap 保证有序
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/Apis/**/**", "anon");
//对所有用户认证
filterChainDefinitionMap.put("/user/loginByUser", "anon"); //用户登录
filterChainDefinitionMap.put("/user/loginByPhone", "anon");//手机号登录
filterChainDefinitionMap.put("/user/getPhoneCode", "anon");//获取手机验证码
filterChainDefinitionMap.put("/user/registered", "anon");//注册
filterChainDefinitionMap.put("/user/forgetPassword", "anon");//忘记密码
//swagger放开过滤
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/v2/**", "anon");
filterChainDefinitionMap.put("/swagger-resources/**", "anon");
filterChainDefinitionMap.put("/static/**", "anon");//静态文件
filterChainDefinitionMap.put("/admin/login", "anon");//登录页面
filterChainDefinitionMap.put("/admin/logout", "logout");//登出页面
//放验证码
filterChainDefinitionMap.put("/captcha/**", "anon");//验证码
// 释放 druid 监控画面
filterChainDefinitionMap.put("/druid/**", "anon");
//释放websocket请求
filterChainDefinitionMap.put("/websocket", "anon");
//前端
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/index", "anon");//任务调度暂时放开
filterChainDefinitionMap.put("/quartz/**", "anon");
//
//对所有页面进行认证
filterChainDefinitionMap.put("/**","authc");
return filterChainDefinitionMap;
}
}
其实只需要看到这段代码,我们对这个路径的拦截放开了??
filterChainDefinitionMap.put("/Apis/**/**", "anon");
好了 现在我只需要取解释这个是是干啥的呢
上图:我这里有两个contorller明显我们放开的对apicontroller的拦截,为啥放开呢?因为我们要使用我自定义的拦截取处理这些请求,而apicontroller里我全装了手机接口,并且添加了前缀
好了 现在我们在重启项目,在不登陆的时候直接访问swagger 地址,模拟手机未登录请求/session获取导致返回login.html的场景
surprise!!!!!!
再模拟一下网页未登录访问页面,首先我们登录一个网页
然后另外开一个窗口,复制网址
再另外一个窗口中点击退出登录
然后回来当前窗口随机点击菜单
对于web 请求直接退回了登录页面login.html
完美解决
当然这是一个前后不分离的框架,同时还对接了手机接口,没办法我这小公司,条件不允许,前后台都是我做,我一个老实巴交的后台真不会前端,索性选了一个自己会点儿的框架,条件允许,并且前后分离的框架完全没有这些困扰
也有一个问题 ----就是我菜啊······哈哈··