用户登录后生成token返回前端。前端每次请求时都在Header中添加token,后端验证token
一、禁用shiro的session
public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
@Override
public Subject createSubject(SubjectContext context) {
context.setSessionCreationEnabled(false);//关闭session创建
return super.createSubject(context);
}
}
二、重写filter过滤器
@Slf4j
public class AuthenFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
boolean flag = false;
HttpServletRequest servletRequest = WebUtils.toHttp(request);
String token = servletRequest.getHeader("token");
if (token != null) {
Claims claims = null;
try {
claims = JwtUtil.parsetJwt(token);
this.getSubject(request, response).login(new JwtToken(token));
} catch (Exception throwable) {
String msg = null;
if (throwable instanceof SignatureException) {
// 该异常为JWT的AccessToken认证失败(Token或者密钥不正确)
msg = "Token或者密钥不正确(" + throwable.getMessage() + ")";
} else if (throwable instanceof ExpiredJwtException) {
msg = "Token已过期(" + throwable.getMessage() + ")";
} else {
msg = throwable.getMessage();
}
// Token认证失败直接返回Response信息
this.response401(response, msg);
}
if (claims != null && claims.getExpiration().getTime() > System.currentTimeMillis()) flag = true;
}
return flag;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
HttpServletResponse servletResponse = WebUtils.toHttp(response);
servletResponse.getWriter().write(JsonUtil.returnJson(4001, "未认证", null));
return false;
}
/**
* 无需转发,直接返回Response信息
*/
private void response401(ServletResponse response, String msg) {
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
httpServletResponse.setStatus(HttpStatus.OK.value());
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
try (PrintWriter out = httpServletResponse.getWriter()) {
String data = JSON.toJSONString(new ResponseBean(HttpStatus.UNAUTHORIZED.value(), "无权访问(Unauthorized):" + msg, null));
out.append(data);
} catch (IOException e) {
throw new CustomException("直接返回Response信息出现IOException异常:" + e.getMessage());
}
}
}
当isAccessAllowed返回为true时,直接允许访问,返回false时,shiro根据onAccessDenied的返回值决定是否允许访问
三、重写Realm类,防止用户登录后修改地址访问无权限接口
@Component
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
@Lazy
private IBdOrganizeEmployeeService iBdOrganizeEmployeeService;
@Autowired
private IBdSecuritySourcesService iBdSecuritySourcesService;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
private int i = 0;
/**
* 重写shiro权限验证,判断已分配给用户的菜单编码(现在放在ul中)和要请求的 @RequiresPermissions能否对应上,对应不上就是无权限
* @param principalCollection
* @return org.apache.shiro.authz.AuthorizationInfo
* @author yueshibing
* @data 2023/1/30 16:51
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
LoggerFactory.getLogger(MyShiroRealm.class).debug(String.valueOf(i));
Object principal = principalCollection.getPrimaryPrincipal();
//String userName = JwtUtil.parsetJwt(principal.toString()).get("userName").toString();
String userId = JwtUtil.parsetJwt(principal.toString()).get("userId").toString();
//获取菜单url
List<BdSecuritySources> bdSecuritySourcesList = iBdSecuritySourcesService.getBdSecuritySourcesByUserID(userId);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
bdSecuritySourcesList.forEach(p -> {
info.addStringPermission(p.getUl());
});
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
JwtToken token = (JwtToken) authenticationToken;
Object credentials = token.getCredentials();
Claims claims = JwtUtil.parsetJwt(credentials.toString());
Object userName = claims.get("userName");
QueryWrapper<BdOrganizeEmployee> wrapper = new QueryWrapper<>();
wrapper.select("USER_NAME");
wrapper.eq("USER_NAME", userName);
BdOrganizeEmployee adminuser = iBdOrganizeEmployeeService.getOne(wrapper);
Object password = "";
if (adminuser != null) password = token.getCredentials();
return new SimpleAuthenticationInfo(
token.getPrincipal(),
password,
// ByteSource.Util.bytes(username),
this.getName()
);
}
}
判断用户菜单权限,和注解@RequiresPermissions匹配,匹配成功才可以访问,该注解用于控制器或者具体的某个方法上
四、shiro配置类
@Configuration
public class ShiroConfig {
@Bean
@Order(Integer.MAX_VALUE)
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager());
//配置自定义过滤器
Map<String, Filter> filterMap = new HashMap<String, Filter>();
filterMap.put("jwtAuthen", new AuthenFilter());
shiroFilterFactoryBean.setFilters(filterMap);
//配置匹配规则
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login/login", "anon");
filterChainDefinitionMap.put("/login/getLoginCode", "anon");
// filterChainDefinitionMap.put("/login/loginOut", "anon");
filterChainDefinitionMap.put("/**", "jwtAuthen");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SessionsSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//配置自定义realm
securityManager.setRealm(realm());
//关闭session
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
sessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
//配置无session工厂类
securityManager.setSubjectFactory(subjectFactory());
return securityManager;
}
@Bean
public Realm realm() {
MyShiroRealm realm = new MyShiroRealm();
return realm;
}
//配置无状态session工厂类
public StatelessDefaultSubjectFactory subjectFactory() {
return new StatelessDefaultSubjectFactory();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 开启aop注解支持
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SessionsSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}