liugw пре 5 година
родитељ
комит
b84ffe04cc

+ 5
- 0
pom.xml Прегледај датотеку

@@ -186,6 +186,11 @@
<artifactId>hutool-all</artifactId>
<version>4.6.10</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.2.5.RELEASE</version>
</dependency>

</dependencies>


+ 3
- 0
src/main/java/com/xdf/creative/module/controller/SysRegionController.java Прегледај датотеку

@@ -12,6 +12,7 @@ import com.xdf.creative.util.page.PageTool;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@@ -83,6 +84,7 @@ public class SysRegionController extends BaseController {
* 全国行政区域[sys_region]分页列表
*/
@GetMapping("/getPageList")
@RequiresPermissions("/sysRegion/getPageList")
@ApiOperation(value = "获取SysRegion分页列表", notes = "全国行政区域[sys_region]分页列表", response = SysRegionQueryVo.class)
public ApiResult<PageTool<SysRegionQueryVo>> getSysRegionPageList(@Valid @RequestBody SysRegionQueryParam sysRegionQueryParam) throws Exception {
PageTool<SysRegionQueryVo> pageList = sysRegionService.getSysRegionPageList(sysRegionQueryParam);
@@ -95,6 +97,7 @@ public class SysRegionController extends BaseController {
*/
@OperationLogger(value = "获取SysRegion列表") //这里添加了AOP的自定义注解
@PostMapping("/getRegionList")
@RequiresPermissions("/sysRegion/getRegionList")
@ApiOperation(value = "获取SysRegion列表", notes = "全国行政区域[sys_region]分页列表", response = SysRegionQueryVo.class)
public ApiResult<List<SysRegionQueryVo>> getSysRegionList(@Valid @RequestBody SysRegionQueryParam sysRegionQueryParam) throws Exception {
return ApiResult.ok(sysRegionService.getSysRegionList(sysRegionQueryParam));

+ 5
- 6
src/main/java/com/xdf/creative/module/service/impl/SysUserServiceImpl.java Прегледај датотеку

@@ -37,6 +37,7 @@ import com.xdf.creative.util.StringUtil;
import com.xdf.creative.util.UUIDUtil;
import com.xdf.creative.util.convert.SysUserConvert;
import com.xdf.creative.util.page.PageTool;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.collection.CollectionUtil;
import org.apache.shiro.SecurityUtils;
@@ -60,6 +61,7 @@ import java.util.*;
* @author DeanYe
* @since 2019-10-24
*/
@Api
@Slf4j
@Service
public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser> implements SysUserService {
@@ -140,17 +142,14 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser>
String token = JwtUtil.generateToken(sysUser.getId(), userName, sysUser.getOrganizeId(),sysUser.getUserType(), newSalt, Duration.ofSeconds(expireSecond));
log.debug("token:{}", token);
// 创建AuthenticationToken
JwtToken jwtToken = JwtToken.build(token, sysUser.getId(), userName, sysUser.getOrganizeId(),sysUser.getUserType(), newSalt, expireSecond);
JwtToken jwtToken = new JwtToken(token);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
loginParam.getUsername(),
loginParam.getPassword());
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
subject.login(jwtToken);

// 返回token和登陆用户信息对象
LoginSysUserTokenVo loginSysUserTokenVo = new LoginSysUserTokenVo();
loginSysUserTokenVo.setToken("12wer234f345g34g45");
loginSysUserTokenVo.setToken(token);
loginSysUserTokenVo.setLoginSysUserVo(loginSysUserVo);
return loginSysUserTokenVo;
}

+ 52
- 10
src/main/java/com/xdf/creative/shiro/CustomRealm.java Прегледај датотеку

@@ -1,11 +1,18 @@
package com.xdf.creative.shiro;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.xdf.creative.base.vo.creative.SysRoleQueryVo;
import com.xdf.creative.enums.ApiCode;
import com.xdf.creative.module.entity.SysUser;
import com.xdf.creative.module.service.SysPermissionService;
import com.xdf.creative.module.service.SysRoleService;
import com.xdf.creative.module.service.SysUserService;
import com.xdf.creative.shiro.jwt.JwtToken;
import com.xdf.creative.shiro.jwt.JwtUtil;
import com.xdf.creative.support.exception.BusinessException;
import com.xdf.creative.util.StringUtil;
import com.xdf.creative.util.page.ApiResult;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
@@ -17,6 +24,8 @@ import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class CustomRealm extends AuthorizingRealm {

private final static Logger log = LoggerFactory.getLogger(AuthorizingRealm.class);
@@ -25,16 +34,30 @@ public class CustomRealm extends AuthorizingRealm {
private SysUserService sysUserService;
@Autowired
private SysRoleService sysRoleService;
@Autowired
private SysPermissionService sysPermissionService;

/**
* 必须重写此方法,不然Shiro会报错
*
* @param token
* @return
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}


@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
String token = (String) principalCollection.getPrimaryPrincipal();
//根据用户名去数据库查询用户信息
SysUser user = null;
try {
user = sysUserService.getSysUserByUserName(name);
if(null==user){
user = sysUserService.getSysUserByUserName(JwtUtil.getUsername(token));
if (null == user) {
return null;
}
} catch (Exception e) {
@@ -46,7 +69,7 @@ public class CustomRealm extends AuthorizingRealm {
HashSet<String> roleNameSet = new HashSet<>();
HashSet<String> roleCodeSet = new HashSet<>();

List<SysRoleQueryVo> sysRoleQueryVoList = sysRoleService.getUserRoleByUserId(user.getId());
List<SysRoleQueryVo> sysRoleQueryVoList = sysRoleService.getUserRoleByUserId(JwtUtil.getUserId(token));
if (CollectionUtil.isEmpty(sysRoleQueryVoList)) {
throw new BusinessException("角色不存在");
}
@@ -56,7 +79,13 @@ public class CustomRealm extends AuthorizingRealm {
roleCodeSet.add(sysRoleQueryVo.getCode());
});
simpleAuthorizationInfo.setRoles(roleCodeSet);
// simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
Set<String> permissionSrt = null;
try {
permissionSrt = sysPermissionService.getPermissionUrlByUserId(JwtUtil.getUserId(token));
} catch (Exception e) {
throw new BusinessException("资源不存在");
}
simpleAuthorizationInfo.setStringPermissions(permissionSrt);
return simpleAuthorizationInfo;
}

@@ -66,15 +95,26 @@ public class CustomRealm extends AuthorizingRealm {
if (authenticationToken.getPrincipal() == null) {
return null;
}
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String token = (String) authenticationToken.getCredentials();
//token失效
if (JwtUtil.isExpired(token)) {
log.error("令牌过期");
String result = JSON.toJSONString(ApiResult.fail(ApiCode.TOKEN_INVALID));
throw new AuthenticationException(result);
}
String userName = JwtUtil.getUsername(token);
if (StringUtil.isEmpty(userName)) {
log.error("非法令牌");
String result = JSON.toJSONString(ApiResult.fail(ApiCode.TOKEN_INVALID));
throw new AuthenticationException(result);
}
//根据用户名去数据库查询用户信息
SysUser user = null;
//获取用户信息
String name =usernamePasswordToken.getUsername();
try {
user = sysUserService.getSysUserByUserName(name);
user = sysUserService.getSysUserByUserName(userName);
} catch (Exception e) {
log.error("用户 { "+name+" } 不存在 ");
log.error("用户 { " + userName + " } 不存在 ");
throw new AccountException("账户不存在");
}
if (user == null) {
@@ -82,9 +122,11 @@ public class CustomRealm extends AuthorizingRealm {
throw new BusinessException("用户不存在");
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, usernamePasswordToken.getPassword().toString(), getName());
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(token, token, getName());
return simpleAuthenticationInfo;
}
}


}


+ 44
- 13
src/main/java/com/xdf/creative/shiro/ShiroConfig.java Прегледај датотеку

@@ -4,13 +4,17 @@ package com.xdf.creative.shiro;
* @author : lgw
* @date : 15:58 2020/3/3
*/
import com.xdf.creative.shiro.filter.JwtFilter;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -18,7 +22,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
@@ -39,18 +45,27 @@ public class ShiroConfig {
return customRealm;
}

/*
* a. 告诉shiro不要使用默认的DefaultSubject创建对象,因为不能创建Session
* */
@Bean
public SubjectFactory subjectFactory() {
return new StatelessWebSubjectFactory();
}

//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());

// 无状态subjectFactory设置
DefaultSessionStorageEvaluator evaluator = (DefaultSessionStorageEvaluator)((DefaultSubjectDAO) securityManager.getSubjectDAO()).getSessionStorageEvaluator();
evaluator.setSessionStorageEnabled(Boolean.FALSE);
StatelessWebSubjectFactory subjectFactory = new StatelessWebSubjectFactory();
securityManager.setSubjectFactory(subjectFactory);
SecurityUtils.setSecurityManager(securityManager);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
//禁止Subject的getSession方法
securityManager.setSubjectFactory(subjectFactory());
return securityManager;
}

@@ -58,19 +73,33 @@ public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
//登出
map.put("/logout", "logout");
//对所有用户认证
map.put("/**", "authc");
shiroFilterFactoryBean.setSecurityManager(securityManager());
//登录
shiroFilterFactoryBean.setLoginUrl("/sysUser/login");
//首页
shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

/*
* c. 添加jwt过滤器,并在下面注册
* 也就是将jwtFilter注册到shiro的Filter中
* 指定除了login和logout之外的请求都先经过jwtFilter
* */
Map<String, Filter> filterMap = new HashMap<>();
//这个地方其实另外两个filter可以不设置,默认就是
filterMap.put("anon", new AnonymousFilter());
filterMap.put("jwt", new JwtFilter());
filterMap.put("logout", new LogoutFilter());
shiroFilterFactoryBean.setFilters(filterMap);

// 拦截器
Map<String, String> filterRuleMap = new LinkedHashMap<>();
filterRuleMap.put("/sysUser/login", "anon");
filterRuleMap.put("/logout", "logout");
filterRuleMap.put("/**", "jwt");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterRuleMap);

return shiroFilterFactoryBean;
}
/**
@@ -100,4 +129,6 @@ public class ShiroConfig {
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}


}

+ 0
- 2
src/main/java/com/xdf/creative/shiro/StatelessWebSubjectFactory.java Прегледај датотеку

@@ -18,6 +18,4 @@ public class StatelessWebSubjectFactory extends DefaultWebSubjectFactory {
return super.createSubject(context);
}

public StatelessWebSubjectFactory() {}

}

+ 174
- 0
src/main/java/com/xdf/creative/shiro/filter/JwtFilter.java Прегледај датотеку

@@ -0,0 +1,174 @@
package com.xdf.creative.shiro.filter;

/**
* @author : lgw
* @date : 10:46 2020/3/4
*/

import com.alibaba.fastjson.JSON;
import com.xdf.creative.enums.ApiCode;
import com.xdf.creative.shiro.jwt.JwtProperties;
import com.xdf.creative.shiro.jwt.JwtToken;
import com.xdf.creative.util.page.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.Filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;


/**
* JwtFilter:jwt过滤器来作为shiro的过滤器
*
* @author zhangxiaoxiang
* @date: 2019/07/12
*/
@Slf4j
@Component//这个注入与否影响不大
public class JwtFilter extends BasicHttpAuthenticationFilter implements Filter {

@Autowired
private JwtProperties jwtProperties;

@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String token = req.getHeader("Authorization");
return token != null;
}

/**
* 返回结果为true表明登录通过
*/
@Override
protected boolean executeLogin(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
log.warn("onAccessDenied 方法被调用");
//这个地方和前端约定,要求前端将jwtToken放在请求的Header部分
//所以以后发起请求的时候就需要在Header中放一个Authorization,值就是对应的Token
HttpServletRequest request = (HttpServletRequest) servletRequest;
String token = request.getHeader("Authorization");
if (null == token) {
return false;
}
log.info("请求的 Header 中藏有 jwtToken {}", token);
JwtToken jwtToken = new JwtToken(token);
try {
// 委托 realm 进行登录认证
//所以这个地方最终还是调用JwtRealm进行的认证
getSubject(servletRequest, servletResponse).login(jwtToken);
//也就是subject.login(token)
} catch (Exception e) {
e.printStackTrace();
onLoginFail(servletResponse);
return false;
}
return true;
//执行方法中没有抛出异常就表示登录成功
}

//登录失败时默认返回 401 状态码
private void onLoginFail(ServletResponse response) throws IOException {
ApiResult result = new ApiResult();
result.setCode(ApiCode.TOKEN_INVALID.getCode());
result.setMsg(ApiCode.TOKEN_INVALID.getMsg());
getResponseResult(response,result);
}

/**
* 这里我们详细说明下为什么重写 可以对比父类方法,只是将executeLogin方法调用去除了
* 如果没有去除将会循环调用doGetAuthenticationInfo方法
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
this.sendChallenge(request, response);
return false;
}

/**
* 执行登录认证
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginAttempt(request, response)) {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
executeLogin(request, response);
} catch (Exception e) {
log.error("JwtFilter过滤验证失败!");
ApiResult result = new ApiResult();
result.setCode(ApiCode.NOT_PERMISSION.getCode());
result.setMsg("项目权限不足");
getResponseResult(response,result);
//throw new AuthorizationException("项目权限不足", e);
}
return true;
}else {
ApiResult result = new ApiResult();
result.setCode(ApiCode.TOKEN_INVALID.getCode());
result.setMsg("无效Token请求");
getResponseResult(response,result);
return false;
}
}

/**
* 返回结果
* @param response
*/
public void getResponseResult(ServletResponse response,ApiResult result){
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
PrintWriter printWriter = null;
try {
printWriter = WebUtils.toHttp(response).getWriter();
printWriter.write(JSON.toJSONString(result));
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (null != printWriter) {
printWriter.close();
}
}
}


/**
* 对跨域提供支持
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}

+ 14
- 64
src/main/java/com/xdf/creative/shiro/jwt/JwtToken.java Прегледај датотеку

@@ -5,6 +5,7 @@ import com.auth0.jwt.interfaces.DecodedJWT;
import com.xdf.creative.util.IpUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.shiro.authc.AuthenticationToken;

import java.util.Date;

@@ -17,75 +18,24 @@ import java.util.Date;
**/
@Data
@Accessors(chain = true)
public class JwtToken {
public class JwtToken implements AuthenticationToken {

private Long userId;
/**
* 登陆ip
*/
private String host;
/**
* 登陆用户名称
*/
private String username;

/***
* 组织id
*/
private Long organizeId;

/***
* 组织类型 0 普通用户 1、园区用户 2 区文创办 3市文创办 8后台管理
*/
private Integer organizeType;

/**
* 辖区id
*/
private String regionId;

/**
* 登陆盐值
*/
private String salt;
/**
* 登陆token
*/
private static final long serialVersionUID = 1L;
// 秘钥
private String token;
/**
* 创建时间
*/
private Date createDate;
/**
* 多长时间过期,默认一小时
*/
private long expireSecond;
/**
* 过期日期
*/
private Date expireDate;

private String principal;

private String credentials;

public JwtToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}

public static JwtToken build(String token, Long userId, String username, Long organizeId, Integer organizeType, String salt, long expireSecond) {
DecodedJWT decodedJWT = JwtUtil.getJwtInfo(token);
Date createDate = decodedJWT.getIssuedAt();
Date expireDate = decodedJWT.getExpiresAt();
return new JwtToken()
.setUsername(username)
.setUserId(userId)
.setToken(token)
.setOrganizeType(organizeType)
.setOrganizeId(organizeId)
.setHost(IpUtil.getRequestIp())
.setSalt(salt)
.setCreateDate(createDate)
.setExpireSecond(expireSecond)
.setExpireDate(expireDate);

@Override
public Object getCredentials() {
return token;
}

}

+ 1
- 0
src/main/resources/application.yml Прегледај датотеку

@@ -71,6 +71,7 @@ spring:


############################ JWT start #############################
spring-boot-plus:
jwt:
# token请求头名称
token-name: token

Loading…
Откажи
Сачувај