最新文章
springboot集成Security使用token实现认证授权(二) 认证授权
......接上文“springboot集成Security使用token实现认证授权(一) 准备篇”security的认证授权配置类:@Configuration//开启security@EnableWebSecurity//开启方法级注解支持@EnableGlobalMethodSecurity(prePostEnabled = true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Autowired private RsaProperties rsaProperties; /** * 加密对象放入ioc容器 * * @return */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 加密策略 * * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder()); } /** * 全新啊配置 * * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() //.antMatchers().permitAll() //需认证后访问的方法 .anyRequest() .authenticated() .and() //认证过滤器 .addFilter(new UserAuthenticationFilter(authenticationManager(), rsaProperties)) //token验证过滤器 .addFilter(new TokenVerifyFilter(authenticationManager(), rsaProperties)) //禁用session .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() //关闭csrf .csrf().disable() .cors().disable() ; }} 认证过滤器:public class UserAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; private RsaProperties rsaProperties; public UserAuthenticationFilter(AuthenticationManager authenticationManager, RsaProperties rsaProperties) { this.authenticationManager = authenticationManager; this.rsaProperties = rsaProperties; } /** * 获取用户名密码 * * @param request * @param response * @return * @throws AuthenticationException */ @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { UserPo userPo = null; try { //获取用户信息 userPo = new ObjectMapper().readValue(request.getInputStream(), UserPo.class); //认证 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userPo.getUsername(), userPo.getPassword()); Authentication authentication = authenticationManager.authenticate(authRequest); return authentication; } catch (IOException e) { DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "用户名或密码错误")); } throw new BaseException("登录异常"); } /** * 用户名密码授权成功返货token * * @param request * @param response * @param chain * @param authResult * @throws IOException * @throws ServletException */ @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { //从authResult获取认证成功的用户信息 UserPo authUser = (UserPo) authResult.getPrincipal(); authUser.setPassword(null); //生成token String token = JwtUtil.generateTokenExpireInMinutes(authUser, rsaProperties.getPrivateKey(), 10); //将token写入header response.addHeader("Authorization", "Bearer " + token); DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.success(ResponseCodeAndMsgEnum.SUCCESS.getCode(), "登录成功")); } /** * 认证失败 * * @param request * @param response * @param failed * @throws IOException * @throws ServletException */ @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "用户名或密码错误")); }} token检测过滤器:public class TokenVerifyFilter extends BasicAuthenticationFilter { private RsaProperties rsaProperties; public TokenVerifyFilter(AuthenticationManager authenticationManager, RsaProperties rsaProperties) { super(authenticationManager); this.rsaProperties = rsaProperties; } /** * 过滤请求 * * @param request * @param response * @param chain */ @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { try { //请求体的头中是否包含Authorization String header = request.getHeader("Authorization"); //Authorization中是否包含Bearer,不包含直接返回 if (header == null || !header.startsWith("Bearer ")) { chain.doFilter(request, response); DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "请登录!")); return; } //获取权限失败,会抛出异常 UsernamePasswordAuthenticationToken authentication = getAuthentication(request); //获取后,将Authentication写入SecurityContextHolder中供后序使用 SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); } catch (Exception e) { DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "请登录!")); e.printStackTrace(); } } /** * 通过token,获取用户信息 * * @param request * @return */ private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { String token = request.getHeader("Authorization"); if (token != null) { //通过token解析出载荷信息 Payload<UserPo> payload = JwtUtil.getInfoFromToken(token.replace("Bearer ", ""), rsaProperties.getPublicKey(), UserPo.class); UserPo user = payload.getUserInfo(); //不为null,返回 if (user != null) { return new UsernamePasswordAuthenticationToken(user, null, user.getRoleList()); } return null; } return null; }} 编写controller的测试请求:@RestController@RequestMapping("/orderTest")public class OrderController { //拥有ROLE_ORDER角色才能访问 @PreAuthorize(value = "hasAnyRole('ROLE_ORDER')") @PostMapping("/list") public String list() { return "订单列表查询成功"; }} @RestController@RequestMapping("/productTest")public class ProductController {//拥有ROLE_PRODUCT角色才能访问 @PreAuthorize(value = "hasAnyRole('ROLE_PRODUCT')") @GetMapping("/list") public String list(){ return "产品列表访问成功"; }//登录即可访问 @GetMapping("/detail") public String detail(){ return "产品详情访问成功"; }} 测试我们使用postman进行测试,启动项目后调用login请求(login请求是security自带的,不需要我们编写)登录,登录成功后在响应的header中获取token。如图:访问其他需要认证的接口时需要将token放入请求头中。如图:如果登录用户没有该接口的访问权限,程序将出现异常,提示你不允许访问。而像访问/productTest/detail这样的接口就能正常访问。 本文主要在于演示security如何实现认证授权,如果对文中代码有疑问或者有什么建议的同学欢迎留言或添加好友交流。
Apr 15, 2020 10:55:04 PM
400
springboot集成Security使用token实现认证授权(一) 准备篇
springsecurity是企业中经常用到的安全控制框架,为我们提供认证授权的功能,由于security与sping无缝对接,让程序员的开发简单灵活。本文将简单地演示一下springsecurity在springboot项目中如何实现基于token的认证授权。本文主要参考博客: https://www.cnblogs.com/ifme/p/12184587.htmlRBACRBAC是基于角色的访问控制(Role-Based Access Control )。在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。相当于权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。本文主要讲解通过角色来控制用户访问方法的权限。建表语句SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`permission_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`permission_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限名称',
`permission_desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限描述',
PRIMARY KEY (`permission_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '权限表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`role_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名称',
`role_desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色说明',
INDEX `role_id`(`role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_role_permission_auth
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission_auth`;
CREATE TABLE `sys_role_permission_auth` (
`role_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色id',
`permission_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限id',
UNIQUE INDEX `role_id`(`role_id`, `permission_id`) USING BTREE,
INDEX `permission_id`(`permission_id`) USING BTREE,
CONSTRAINT `sys_role_permission_auth_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `sys_role_permission_auth_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `sys_permission` (`permission_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色权限关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`state` int(2) NOT NULL COMMENT '状态。1正常,2删除',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_user_role_auth
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role_auth`;
CREATE TABLE `sys_user_role_auth` (
`user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户',
`role_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色',
UNIQUE INDEX `user_id`(`user_id`, `role_id`) USING BTREE,
INDEX `role_id`(`role_id`) USING BTREE,
CONSTRAINT `sys_user_role_auth_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `sys_user_role_auth_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户角色关系表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1; 代码实现搭建maven工程pom依赖<!-- 安全框架 --><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId></dependency><!-- 由于后面要用springcloud继续优化,所以选择了cloud-stater --><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId></dependency><!-- jwt --><dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.10.7</version></dependency><dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.10.7</version> <scope>runtime</scope></dependency><dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.10.7</version> <scope>runtime</scope></dependency><!-- 获取配置信息 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional></dependency><!-- lombok插件 --><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency><!-- 时间工具包 --><dependency> <groupId>com.bluecatcode.time</groupId> <artifactId>joda-time-2.1-extended</artifactId> <version>1.1.0</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!-- mybatis --><dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version></dependency><!-- mysql连接工具 --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency> 启动类@SpringBootApplication//扫描dao接口@MapperScan(basePackages = {"work.chenchuan.providertestone.**.dao"})//开启事务管理@EnableTransactionManagementpublic class ProviderTestOneApplication { public static void main(String[] args) { SpringApplication.run(ProviderTestOneApplication.class, args); }} 导入工具类json工具类:public class JsonUtil { public static final ObjectMapper mapper = new ObjectMapper(); private static final Logger logger = LoggerFactory.getLogger(JsonUtil.class); public static String toString(Object obj) { if (obj == null) { return null; } if (obj.getClass() == String.class) { return (String) obj; } try { return mapper.writeValueAsString(obj); } catch (JsonProcessingException e) { logger.error("json序列化出错:" + obj, e); return null; } } public static <T> T toBean(String json, Class<T> tClass) { try { return mapper.readValue(json, tClass); } catch (IOException e) { logger.error("json解析出错:" + json, e); return null; } } public static <E> List<E> toList(String json, Class<E> eClass) { try { return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass)); } catch (IOException e) { logger.error("json解析出错:" + json, e); return null; } } public static <K, V> Map<K, V> toMap(String json, Class<K> kClass, Class<V> vClass) { try { return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass)); } catch (IOException e) { logger.error("json解析出错:" + json, e); return null; } } public static <T> T nativeRead(String json, TypeReference<T> type) { try { return mapper.readValue(json, type); } catch (IOException e) { logger.error("json解析出错:" + json, e); return null; } }} jwt工具类:public class JwtUtil { private static final String JWT_PAYLOAD_USER_KEY = "user"; /** * 私钥加密token * * @param userInfo 载荷中的数据 * @param privateKey 私钥 * @param expire 过期时间,单位分钟 * @return JWT */ public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) { return Jwts.builder() .claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo)) .setId(createJTI()) .setExpiration(DateTime.now().plusMinutes(expire).toDate()) .signWith(privateKey, SignatureAlgorithm.RS256) .compact(); } /** * 私钥加密token * * @param userInfo 载荷中的数据 * @param privateKey 私钥 * @param expire 过期时间,单位秒 * @return JWT */ public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) { return Jwts.builder() .claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo)) .setId(createJTI()) .setExpiration(DateTime.now().plusSeconds(expire).toDate()) .signWith(privateKey, SignatureAlgorithm.RS256) .compact(); } /** * 公钥解析token * * @param token 用户请求中的token * @param publicKey 公钥 * @return Jws<Claims> */ private static Jws<Claims> parserToken(String token, PublicKey publicKey) { return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token); } private static String createJTI() { return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes())); } /** * 获取token中的用户信息 * * @param token 用户请求中的令牌 * @param publicKey 公钥 * @return 用户信息 */ public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) { Jws<Claims> claimsJws = parserToken(token, publicKey); Claims body = claimsJws.getBody(); Payload<T> claims = new Payload<>(); claims.setId(body.getId()); claims.setUserInfo(JsonUtil.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType)); claims.setExpiration(body.getExpiration()); return claims; } /** * 获取token中的载荷信息 * * @param token 用户请求中的令牌 * @param publicKey 公钥 * @return 用户信息 */ public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) { Jws<Claims> claimsJws = parserToken(token, publicKey); Claims body = claimsJws.getBody(); Payload<T> claims = new Payload<>(); claims.setId(body.getId()); claims.setExpiration(body.getExpiration()); return claims; }} rsa工具类:public class RsaUtil { private static final int DEFAULT_KEY_SIZE = 2048; /** * 从文件中读取公钥 * * @param filename 公钥保存路径,相对于classpath * @return 公钥对象 * @throws Exception */ public static PublicKey getPublicKey(String filename) throws Exception { byte[] bytes = readFile(filename); return getPublicKey(bytes); } /** * 从文件中读取密钥 * * @param filename 私钥保存路径,相对于classpath * @return 私钥对象 * @throws Exception */ public static PrivateKey getPrivateKey(String filename) throws Exception { byte[] bytes = readFile(filename); return getPrivateKey(bytes); } /** * 获取公钥 * * @param bytes 公钥的字节形式 * @return * @throws Exception */ private static PublicKey getPublicKey(byte[] bytes) throws Exception { bytes = Base64.getDecoder().decode(bytes); X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePublic(spec); } /** * 获取密钥 * * @param bytes 私钥的字节形式 * @return * @throws Exception */ private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { bytes = Base64.getDecoder().decode(bytes); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(spec); } /** * 根据密文,生存rsa公钥和私钥,并写入指定文件 * * @param publicKeyFilename 公钥文件路径 * @param privateKeyFilename 私钥文件路径 * @param secret 生成密钥的密文 */ public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); SecureRandom secureRandom = new SecureRandom(secret.getBytes()); keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom); KeyPair keyPair = keyPairGenerator.genKeyPair(); // 获取公钥并写出 byte[] publicKeyBytes = keyPair.getPublic().getEncoded(); publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes); writeFile(publicKeyFilename, publicKeyBytes); // 获取私钥并写出 byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes); writeFile(privateKeyFilename, privateKeyBytes); } private static byte[] readFile(String fileName) throws Exception { return Files.readAllBytes(new File(fileName).toPath()); } private static void writeFile(String destPath, byte[] bytes) throws IOException { File dest = new File(destPath); if (!dest.exists()) { dest.createNewFile(); } Files.write(dest.toPath(), bytes); }} 响应状态码枚举,定于程序响应状态的状态码和状态说明:public enum ResponseCodeAndMsgEnum { SUCCESS("200", "请求成功"), FAILE("-1", "请求失败"), SYS_ERROR("500", "服务器内部错误"), UNAUTHORIZED("401", "没有权限访问该资源"), //FORBIDDEN("403", "没有权限访问该资源"), NOT_FOUND("404", "访问的资源不存在"), SERVICE_UNAVAILABLE("503","相关服务器异常,触发熔断"); /** * 状态码 */ private String code; /** * 消息提示 */ private String msg; ResponseCodeAndMsgEnum(String code, String msg) { this.code = code; this.msg = msg; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }} controller格式统一返回的属性:public class JsonResult<T> { /** * 访问状态 */ private Boolean state; /** * 响应状态码 */ private String code; /** * 响应提示信息 */ private String msg; /** * 返回数据 */ private T data; /** * 请求路径 */ private String uri; /** * 当前时间 */ private Long timestamp; public Boolean getState() { return state; } public void setState(Boolean state) { this.state = state; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public Long getTimestamp() { return timestamp; } public void setTimestamp(Long timestamp) { this.timestamp = timestamp; }} controller统一格式返回工具类:public class JsonResponseUtil { /** * 请求成功默认 */ private static final ResponseCodeAndMsgEnum SUCCESS = ResponseCodeAndMsgEnum.SUCCESS; /** * 请求失败默认 */ private static final ResponseCodeAndMsgEnum FAILE = ResponseCodeAndMsgEnum.FAILE; /** * 系统服务错误映射 */ private static final ResponseCodeAndMsgEnum SYS_ERROR = ResponseCodeAndMsgEnum.SYS_ERROR; /** * 未授权映射 */ private static final ResponseCodeAndMsgEnum UNAUTHORIZED = ResponseCodeAndMsgEnum.UNAUTHORIZED; /** * 未找到资源映射 */ private static final ResponseCodeAndMsgEnum NOT_FOUND = ResponseCodeAndMsgEnum.NOT_FOUND; /** * 服务器由于维护或者负载过重未能应答或相关服务器异常,触发熔断 */ private static final ResponseCodeAndMsgEnum SERVICE_UNAVAILABLE = ResponseCodeAndMsgEnum.SERVICE_UNAVAILABLE; /** * 成功无数据——默认 * * @return json对象 */ public static JsonResult success() { JsonResult jsonResult = new JsonResult(); jsonResult.setState(true); jsonResult.setCode(SUCCESS.getCode()); jsonResult.setMsg(SUCCESS.getMsg()); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 成功无数据——自定义code、msg * * @param code * @param msg * @return */ public static JsonResult success(String code, String msg) { JsonResult jsonResult = new JsonResult(); jsonResult.setState(true); jsonResult.setCode(code); jsonResult.setMsg(msg); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 成功有数据——默认 * * @param o * @return */ public static JsonResult success(Object o) { JsonResult jsonResult = new JsonResult(); jsonResult.setState(true); jsonResult.setCode(SUCCESS.getCode()); jsonResult.setMsg(SUCCESS.getMsg()); jsonResult.setData(o); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 成功有数据——自定义code、msg * * @param code * @param msg * @param o * @return */ public static JsonResult success(String code, String msg, Object o) { JsonResult jsonResult = new JsonResult(); jsonResult.setState(true); jsonResult.setCode(code); jsonResult.setMsg(msg); jsonResult.setData(o); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 失败无数据——默认 * * @return */ public static JsonResult fail() { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(FAILE.getCode()); jsonResult.setMsg(FAILE.getMsg()); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 失败无数据——自定义code、msg * * @param code * @param msg * @return */ public static JsonResult fail(String code, String msg) { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(code); jsonResult.setMsg(msg); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 失败有数据——默认 * * @return */ public static JsonResult fail(Object o) { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(FAILE.getCode()); jsonResult.setMsg(FAILE.getMsg()); jsonResult.setData(o); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 失败有数据——自定义code、msg * * @param code * @param msg * @param o * @return */ public static JsonResult fail(String code, String msg, Object o) { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(code); jsonResult.setMsg(msg); jsonResult.setData(o); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 内部服务器错误 * * @return */ public static JsonResult sysError() { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(SYS_ERROR.getCode()); jsonResult.setMsg(SYS_ERROR.getMsg()); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 没有权限访问资源 * * @return */ public static JsonResult unauthorized() { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(UNAUTHORIZED.getCode()); jsonResult.setMsg(UNAUTHORIZED.getMsg()); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 资源不存在 * * @return */ public static JsonResult notFound() { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(NOT_FOUND.getCode()); jsonResult.setMsg(NOT_FOUND.getMsg()); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 服务器由于维护或者负载过重未能应答或相关服务器异常,触发熔断 * ————默认 * * @return */ public static JsonResult unavailable() { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(SERVICE_UNAVAILABLE.getCode()); jsonResult.setMsg(SERVICE_UNAVAILABLE.getMsg()); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; } /** * 服务器由于维护或者负载过重未能应答或相关服务器异常,触发熔断 * ————msg自定义 * * @param msg * @return */ public static JsonResult unavailable(String msg) { JsonResult jsonResult = new JsonResult(); jsonResult.setState(false); jsonResult.setCode(SERVICE_UNAVAILABLE.getCode()); jsonResult.setMsg(msg); jsonResult.setData(null); jsonResult.setTimestamp(new Date().getTime()); return jsonResult; }} 自定义基础异常类:public class BaseException extends RuntimeException { /** * 状态码 */ private String code; /** * 默认异常信息 * * @param jsonResult code、msg枚举 */ public BaseException(JsonResult jsonResult) { super(jsonResult.getMsg()); this.code = jsonResult.getCode(); } /** * 自定义msg * * @param message */ public BaseException(String message) { super(message); this.code = ResponseCodeAndMsgEnum.FAILE.getCode(); } /** * 自定义code、msg * * @param code 状态码 * @param msg 消息提示 */ public BaseException(String code, String msg) { super(msg); this.code = code; } public String getCode() { return code; } public void setCode(String code) { this.code = code; }} 全局异常捕获:@RestControllerAdvicepublic class GlobalExceptionHandler { /** * 自定义异常处理程序 * * @param request * @param e * @return */ @ExceptionHandler(value = RuntimeException.class) public JsonResult gloabalExceptionHandler(HttpServletRequest request, RuntimeException e) { e.printStackTrace(); //异常状态码和消息提示,初始化未定义的异常信息为500 ResponseCodeAndMsgEnum responseCodeAndMsgEnum = ResponseCodeAndMsgEnum.SYS_ERROR; String code = responseCodeAndMsgEnum.getCode(); String msg = responseCodeAndMsgEnum.getMsg(); //请求路径 String uri = request.getRequestURI(); //自定义异常code、msg if (e instanceof BaseException) { code = ((BaseException) e).getCode(); msg = e.getMessage(); } JsonResult jsonResult = JsonResponseUtil.fail(code, msg); jsonResult.setUri(uri); return jsonResult; }} 数据写入响应的工具类:public class DataToHttpServletResponseUtil { /** * 返回的统一json格式数据写入响应 * * @return */ public static void msgToResponseWhenSuccess(HttpServletResponse response, JsonResult jsonResult) { try { response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); PrintWriter out = response.getWriter(); out.write(new ObjectMapper().writeValueAsString(jsonResult)); out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } }} 配置yml配置server: port: 9005spring: application: # 服务别名 name: provider-test-one # 数据源 datasource: url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncodeing=utf-8&serverTimezone=GMT%2b8 username: root password: 123mybatis: ## mybatis扫描xml路径 mapper-locations: classpath:providertestone/mybatis/mapper/**/*.xml# jwt、rsa加密rsa: key: private-key-file: D:\P\ideaProjects\mine\springcloud-frame-v1\jwtRsaKey\private.key public-key-file: D:\P\ideaProjects\mine\springcloud-frame-v1\jwtRsaKey\public.pub 分对称性加密配置@ConfigurationProperties(prefix = "rsa.key")@Component@Datapublic class RsaProperties { /** * 私秘钥文件 */ private String privateKeyFile; /** * 公钥文件 */ private String publicKeyFile; /** * 公钥 */ private PublicKey publicKey; /** * 私钥 */ private PrivateKey privateKey; @PostConstruct public void createRsaKey() throws Exception { publicKey = RsaUtil.getPublicKey(publicKeyFile); privateKey = RsaUtil.getPrivateKey(privateKeyFile); }} 实体类用户:@Datapublic class UserPo extends BasePo implements UserDetails { private String userId; private String username; private String password; /** * 状态 */ private Integer state; /** * 角色集合 */ private List<RolePo> roleList = new ArrayList<>(); @JsonIgnore @Override public Collection<? extends GrantedAuthority> getAuthorities() { return roleList; } /** * 账户是否过期 * @return */ @Override @JsonIgnore public boolean isAccountNonExpired() { return true; } /** * 账户是否锁定 * @return */ @Override @JsonIgnore public boolean isAccountNonLocked() { return true; } /** * 凭证是否可用 * @return */ @Override @JsonIgnore public boolean isCredentialsNonExpired() { return true; } /** * 账户是否启用 * @return */ @Override @JsonIgnore public boolean isEnabled() { return true; }} 角色:@Datapublic class RolePo extends BasePo implements GrantedAuthority { private String roleId; private String roleName; private String roleDesc; /** * 权限列表(本次暂时没有使用权限列表) */ private List<PermissionPo> permissionList = new ArrayList<>(); //标记此属性不做json处理 @JsonIgnore @Override public String getAuthority() { return roleName; }} 权限:@Datapublic class PermissionPo extends BasePo { private String permissionId; private String permissionName; private String permissionDesc;} 用户角色查询user的mapper文件,查询用户的角色:mapper对应的接口忽略。<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.2//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="work.chenchuan.providertestone.sys.dao.UserDao"> <sql id="userObjResult"> u.user_id AS userId, u.username AS username, u.password AS password, u.state AS state </sql> <!-- 用户权限集合 --> <resultMap id="userAndPermissionAuth" type="work.chenchuan.providertestone.sys.po.UserPo"> <id column="userId" property="userId"/> <result column="username" property="username"/> <result column="password" property="password"/> <result column="state" property="state"/> <collection property="roleList" ofType="work.chenchuan.providertestone.sys.po.RolePo"> <id column="roleId" property="roleId"/> <result column="roleName" property="roleName"/> <result column="roleDesc" property="roleDesc"/> <collection property="permissionList" ofType="work.chenchuan.providertestone.sys.po.PermissionPo"> <id column="permissionId" property="permissionId"/> <result column="permissionName" property="permissionName"/> <result column="permissionDesc" property="permissionDesc"/> </collection> </collection> </resultMap> <!-- 根据登录信息获取用户详情 --> <select id="getUserByLoginInfo" parameterType="work.chenchuan.providertestone.sys.po.UserPo" resultMap="userAndPermissionAuth"> SELECT <include refid="userObjResult"/>, <include refid="work.chenchuan.providertestone.sys.dao.RoleDao.roleObjResult"/>, <include refid="work.chenchuan.providertestone.sys.dao.PermissionDao.permissionObjResult"/> FROM `sys_user` u LEFT JOIN sys_user_role_auth ura ON u.user_id = ura.user_id LEFT JOIN sys_role r ON ura.role_id = r.role_id LEFT JOIN sys_role_permission_auth rpa ON r.role_id = rpa.role_id LEFT JOIN sys_permission p ON rpa.permission_id = p.permission_id <where> AND u.username = #{username} </where> </select></mapper> UserService实现类:service接口忽略。@Servicepublic class UserServiceImpl implements UserService { @Autowired private UserDao userDao; /** * 用户认证 * * @param s * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { UserPo userPo = new UserPo(); userPo.setUsername(s); userPo = userDao.getUserByLoginInfo(userPo); if (userPo == null) { return null; } return userPo; }}接下文“springboot集成Security使用token实现认证授权(二) 认证授权”......
Apr 15, 2020 10:11:45 PM
309
springboot2.1整合JPA与mybatis
JPA与mybatis作为持久层框架各有各的优点,比如mybatis的灵活易用,JPA可以少写SQL,方便开发者快速开发。 springboot整合JPA与mybatis现在以查询一张学生表为例,用来验证springboot整合JPA与mybatis的整合结果。学生表如下:1、maven依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!-- mysql链接工具 --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><!-- mybatis --><dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version></dependency><!-- jpa --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!--thymeleaf模板引擎 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency>2、学生实体类package work.chenchuan.study.mybatisjpa.po;import javax.persistence.*;@Entity@Table(name = "student_test")public class StudentPo { /** * 学生编号 */ @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer studentId; /** * 学生姓名 */ private String studentName; /** * 年龄 */ private String age; @Override public String toString() { return "StudentPo{" + "studentId=" + studentId + ", studentName='" + studentName + '\'' + ", age='" + age + '\'' + '}'; } public Integer getStudentId() { return studentId; } public void setStudentId(Integer studentId) { this.studentId = studentId; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } public String getAge() { return age; } public void setAge(String age) { this.age = age; }}3、yml配置spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1/test?characterEncodeing=utf-8&serverTimezone=GMT%2b8 username: root password: 123 jpa: show-sql: true #显示执行sqlmybatis: mapper-locations: classpath:mybatis/**/*.xml #mapper文件存放路径4、整合并验证mybatis1)、创建mapper接口StudentMapper@Repository@Mapperpublic interface StudentMapper { /** * 查询所有学生 * * @return */ List<StudentPo> findAllStudent();}2)、根据yml配置文件创建在对应路径下创建mapper文件<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.2//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="work.chenchuan.study.mybatisjpa.dao.mapper.StudentMapper"> <!-- 查询所有学生 --> <select id="findAllStudent" resultType="work.chenchuan.study.mybatisjpa.po.StudentPo"> SELECT student_id AS studentId, student_name AS studentName, age AS age FROM student_test </select></mapper>3)、测试@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringbootStudyApplicationTests { @Autowired private StudentMapper studentMapper; @Test public void findAllStudent() { System.out.println(studentMapper.findAllStudent()); }}打印结果如下,mybatis整合成功:[StudentPo{studentId=1, studentName='jack', age='12'}, StudentPo{studentId=2, studentName='tom', age='10'}]5、整合并验证JPA1)、创建jpa接口public interface StudentJpaDao extends JpaRepository<StudentPo, Integer> {}2)、测试@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringbootStudyApplicationTests { @Autowired private StudentJpaDao studentJpaDao; @Test public void findOneStudent() { System.out.println(studentJpaDao.findById(1)); }}打印结果如下,JPA整合成功:[StudentPo{studentId=1, studentName='jack', age='12'}] 总结本次是将JPA与mybatis同时整合在一个项目里的,需要注意对于同一个实体,JPA的接口与mybatis的接口要分开单独创建才能同时使用,不然会抛异常。
Sep 11, 2019 1:37:00 PM
598
springboot2.x文件上传下载
springboot集成了springMvc,所以springboot的文件上传与springMvc的文件上传方式基本相同。项目构建项目依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!--thymeleaf模板引擎 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency> yml配置文件spring: thymeleaf: cache: false servlet: multipart: max-file-size: 1MB #文件上传单文件限制大小 max-request-size: 10MB #文件上传一次请求总大小file: upload_path: D:\F\img\upload-test\ #文件上传目标目录单文件上传上传HTML单个文件上传:<form action="/file/uploadOneFile" method="post" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit" value="上传"/></form> 上传Controller//文件上传路径@Value("${file.upload_path}")private String uploadPath; /** * 当文件上传页面 * * @return */@GetMapping("/oneFilePage")public String oneFilePage() { return "/file/oneFilePage";}/** * 单文件上传 * * @param file * @return */@PostMapping("/uploadOneFile")@ResponseBodypublic String uploadOneFile(MultipartFile file) { //判断是否提交文件 if (file.getSize() == 0) { return "请选择上传文件"; } File uploadPathObj = new File(uploadPath); //判断上传目录是否存在 if (!uploadPathObj.exists()) { uploadPathObj.mkdirs(); } //获取文件名 String originalFilename = file.getOriginalFilename(); //上传文件全路径名称 String filePathName = uploadPath + originalFilename; //上传文件对象 File uploadFileObj = new File(filePathName); try { //上传文件 file.transferTo(uploadFileObj); } catch (IOException e) { e.printStackTrace(); return "上传失败"; } return "上传成功";} 选择文件后点击“上传”按钮上传文件,上传成功的提示: 多文件上传上传HTML多个文件上传:<form action="/file/uploadFiles" method="post" enctype="multipart/form-data"> 文件1 : <input type="file" name="files"/><br/> 文件2 : <input type="file" name="files"/><br/> <!--文件3 : <input type="file" name="files"/><br/>--> <input type="submit" value="上传"/></form> 上传Controller//文件上传路径@Value("${file.upload_path}")private String uploadPath; /** * 多文件上传页面 * * @return */@GetMapping("/manyFilePage")public String manyFilePage() { return "/file/manyFilePage";}/** * 多文件上传 * * @param files * @return */@PostMapping("/uploadFiles")@ResponseBodypublic String uploadFiles(MultipartFile[] files) { //判断是否提交文件 if (files == null || files.length == 0) { return "请选择上传文件"; } File uploadPathObj = new File(uploadPath); //判断上传目录是否存在 if (!uploadPathObj.exists()) { uploadPathObj.mkdirs(); } //遍历上文件传 for (MultipartFile file : files) { //获取文件名 String originalFilename = file.getOriginalFilename(); //上传文件全路径名称 String filePathName = uploadPath + originalFilename; //上传文件对象 File uploadFileObj = new File(filePathName); try { //上传文件 file.transferTo(uploadFileObj); } catch (IOException e) { e.printStackTrace(); return "上传失败"; } } return "上传成功";} 选择文件后点击“上传”按钮上传文件,上传成功的提示: 单文件下载下载HTML模拟文件下载:<form action="/file/downloadFile" method="GET"> 下载文件:<input type="text" name="file" value="D:\F\img\upload-test\微信截图_20190826143608.png" readonly/><br> <input type="submit" value="下载"/></form> 下载Controller/** * 单文件下载 * * @param response * @param file * @return */@GetMapping("/downloadFile")@ResponseBodypublic String downloadFile(HttpServletResponse response, String file) { if (file == null) { return "不存在的文件"; } //下载文件对象 File downloadFileObj = new File(file); //文件名 String fileName = downloadFileObj.getName(); //获取字节输出流 try { OutputStream outputStream = response.getOutputStream(); // 自动判断下载文件类型 response.setContentType("multipart/form-data"); //相应编码 response.setCharacterEncoding("utf-8"); // 设置文件名称 response.setHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes("gbk"), "iso8859-1")); //写入输入流 outputStream.write(FileCopyUtils.copyToByteArray(downloadFileObj)); //刷新柳 outputStream.flush(); //关闭流 outputStream.close(); } catch (IOException e) { e.printStackTrace(); return "下载失败"; } return "下载成功";}点击“下载”按钮便可以下载对应路径的文件。
Aug 29, 2019 6:37:48 PM
881
JSOUP爬虫
jsoup是一款Java的HTML解析器,主要用来对HTML解析。参考文档:https://jsoup.org/https://www.open-open.com/jsoup/ 虽然jsoup支持从某个地址直接去爬取网页源码,但是只支持HTTP,HTTPS协议,支持不够丰富。所以,主要还是用来对HTML进行解析,从HTML中抽取自己想要的数据。 利用jsoup爬取汇博网java职位列表数据1、maven依赖<dependencies> <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.12.1</version> </dependency></dependencies> 2、编码获取java职位列表package work.chenchuan.crawler.jsoup;import org.jsoup.Connection;import org.jsoup.Jsoup;import org.jsoup.nodes.Document;import org.jsoup.nodes.Element;import org.jsoup.select.Elements;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class DataFromHuibo { public static void main(String[] args) { //职位列表 List<Map<String, Object>> list = new ArrayList<>(); //获取所有职位列表 list = getAllData(1, list); for (Map<String, Object> cur : list) { System.out.print(cur.get("companyName")); System.out.print(" "); System.out.print(cur.get("name")); System.out.print(" "); System.out.print(cur.get("money")); System.out.print(" "); System.out.print(cur.get("address")); System.out.print(" "); System.out.print(cur.get("exp")); System.out.print(" "); System.out.print(cur.get("detaiUrl")); System.out.print("\n"); } } /** * 获取java职位每页所有列表数据 * * @param p 当前页码 * @param list 职位列表 * @return */ public static List<Map<String, Object>> getAllData(int p, List<Map<String, Object>> list) { //创建并返回URL的连接 Connection connection = Jsoup.connect("https://www.huibo.com/jobsearch/?params=p" + p + "&key=java"); Document document = null; try { //获取html文档 document = connection.get(); //获取列表dom节点 Elements elements = document.select("#job_list_table .postIntro"); //遍历节点获取数据并添加职位列表到list for (Element element : elements) { String companyName = element.select("div.title>a").attr("title"); Elements infoDoms = element.select(".postIntroList .postIntroL"); for (Element infoDom : infoDoms) { String name = infoDom.select(".des_title").text(); String money = infoDom.select(".money").text(); String address = infoDom.select(".address").text(); String exp = infoDom.select(".exp").text(); String detaiUrl = infoDom.select(".name>a").attr("abs:href"); Map<String, Object> curInfo = new HashMap<>(); curInfo.put("companyName", companyName); curInfo.put("name", name); curInfo.put("money", money); curInfo.put("address", address); curInfo.put("exp", exp); curInfo.put("detaiUrl", detaiUrl); list.add(curInfo); } } //当前页公司条数 int curPageSize = elements.size(); //每页职位条数和公司条数不同,当前页公司条数>0查找下一页职位 if (curPageSize > 0) { p++; getAllData(p, list); } } catch (IOException e) { e.printStackTrace(); } return list; } /** * 获取java职位列表首页数据 */ public static void getData() { //创建并返回URL的连接 Connection connection = Jsoup.connect("https://www.huibo.com/jobsearch/?params=p1&key=java×tamp=1566277536#list"); //获取html文档 Document document = null; try { document = connection.get(); //获取列表dom节点 Elements elements = document.select(".postIntroList .clearfix"); //遍历节点获取数据 for (Element element : elements) { //获取节点相关属性 String name = element.select(".des_title").text(); String money = element.select(".money").text(); String address = element.select(".address").text(); String exp = element.select(".exp").text(); System.out.print(name); System.out.print(" "); System.out.print(money); System.out.print(" "); System.out.print(address); System.out.print(" "); System.out.print(exp); System.out.print("\n"); } } catch (IOException e) { e.printStackTrace(); } }} 以上程序打印结果如下:
Aug 21, 2019 2:02:32 PM
615