zoukankan      html  css  js  c++  java
  • Pre后台系统学习笔记

    Docker 打包部署步骤
    1. mvn clean package
    2. 把target下的pre-alpine文件夹下的jar包以及Dockerfile 传输到服务器的某一路径
    3. cd到路径
    4. 构建镜像 docker build -t pre:1.0 .
    5. 运行命令 docker run --name pre -p 8081:8081 -d pre:1.0


    真正的 产品级别 dockerfile文件

    # 基础镜像
    FROM java:openjdk-8-jre-alpine
    # 维护者信息
    MAINTAINER lihaodongmail@163.com
    #Default to UTF-8 file.encoding
    ENV LANG C.UTF-8
    #设置alpine时区
    ENV TIMEZONE Asia/Shanghai
    #alpine自带的包含dl-cdn的域名非常慢,需要修改后才能下载数据。
    RUN apk add -U tzdata && ln -snf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && echo "${TIMEZONE}" > /etc/timezone
    RUN sed -i -e 's/dl-cdn/dl-4/g' /etc/apk/repositories && apk add -U tzdata && ln -snf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && echo "${TIMEZONE}" > /etc/timezone
    #解决验证码问题
    RUN apk add fontconfig && apk add --update ttf-dejavu && fc-cache --force
    #添加应用
    ADD pre-1.0.0-SNAPSHOT.jar pre-1.0.0-SNAPSHOT.jar
    #参数
    #ENV PARAMS=""
    #执行操作 默认启动线上环境
    ENTRYPOINT [ "sh", "-c", "java -Xmx50m -Djava.security.egd=file:/dev/./urandom -jar pre-1.0.0-SNAPSHOT.jar --spring.profiles.active=prod" ]


    3 定义全局异常 除了有 controllerAdvice还有

    @Slf4j
    @RestControllerAdvice
    public class BExceptionHandler {


    /**
    * 处理自定义异常
    */
    @ExceptionHandler(BaseException.class)
    public R handleRRException(BaseException e) {
    return R.error(e.getCode(), e.getMsg());
    }

    lombok的另外一个注解
    重写 hashcode和equal

    @EqualsAndHashCode(callSuper = true)

    4
    设计到了 mybatisplus的学习

    5 使用fastjson和 string作为redistemplate的序列化类

    import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisOperations;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.serializer.StringRedisSerializer;

    /**
    * @Classname RedisConfig
    * @Description redis配置
    * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com
    * @Date 2019-06-19 10:40
    * @Version 1.0
    */
    @Configuration
    @ConditionalOnClass(RedisOperations.class)
    public class RedisConfig {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
    RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<Object, Object> template = new RedisTemplate<>();

    //使用fastjson序列化
    FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<Object>(Object.class);
    // value值的序列化采用fastJsonRedisSerializer
    template.setValueSerializer(fastJsonRedisSerializer);
    // key的序列化采用StringRedisSerializer
    template.setKeySerializer(new StringRedisSerializer());
    template.setConnectionFactory(redisConnectionFactory);
    return template;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
    RedisConnectionFactory redisConnectionFactory) {
    StringRedisTemplate template = new StringRedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
    }

    }

    6 swagger2的使用:

    import com.google.common.collect.Lists;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.ParameterBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.service.Parameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;

    import java.util.ArrayList;

    /**
    * @Classname Swagger2Config
    * @Description Swagger2 配置
    * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com
    * @Date 2019-06-18 14:37
    * @Version 1.0
    */
    @Configuration
    @EnableSwagger2
    public class Swagger2Config {

    @Value("${jwt.header}")
    private String tokenHeader;

    @Bean
    public Docket createRestApi() {
    ParameterBuilder ticketPar = new ParameterBuilder();
    ArrayList<Parameter> pars = Lists.newArrayList();
    ticketPar.name(tokenHeader).description("token")
    .modelRef(new ModelRef("string"))
    .parameterType("header")
    .defaultValue("Bearer ")
    .required(true)
    .build();
    pars.add(ticketPar.build());
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    .select()
    //选择controller包
    .apis(RequestHandlerSelectors.basePackage("com.xd.pre.controller"))
    .paths(PathSelectors.any())
    .build();
    }

    private ApiInfo apiInfo() {
    return new ApiInfoBuilder()
    //自定义信息可按需求填写
    .title("Pre使用Swagger2构建RESTFul APIs")
    .description("测试")
    .termsOfServiceUrl("http://www.52lhd.com")
    .contact(new Contact("小东啊","http://www.52lhd.com","lihaodongmail@163.com"))
    .version("1.0")
    .build();
    }

    }

    7 在枚举上仍然可以使用 lombok,例如如下代码
    分别表示 自动get方法和全 参数的构造函数

    @Getter
    @AllArgsConstructor

    8 首页生成验证码的方式:

    先生成图片,然后获取图片对应的 文字,保持到redis中2分钟,
    图片则保持到本地, ImageIO.write 这种方式 保持图片 到 jpeg,格式
    和imge和 目的地(目的地是流) ServletOutputStream.,
    同时 请求时自带了request和response


    /**
    * 生成验证码
    *
    * @param response
    * @param request
    * @throws ServletException
    * @throws IOException
    */
    @GetMapping("/captcha.jpg")
    public void captcha(HttpServletResponse response, HttpServletRequest request) throws IOException {
    response.setHeader("Cache-Control", "no-store, no-cache");
    response.setContentType("image/jpeg");
    // 生成图片验证码
    BufferedImage image = CaptchaUtil.createImage();
    // 生成文字验证码
    String randomText = CaptchaUtil.drawRandomText(image);
    // 保存到验证码到 redis 有效期两分钟
    redisTemplate.opsForValue().set(PreConstant.PRE_IMAGE_SESSION_KEY, randomText.toLowerCase(),2, TimeUnit.MINUTES);
    ServletOutputStream out = response.getOutputStream();
    ImageIO.write(image, "jpeg", out);
    }

    9 验证码 生成工具类方法都是固定的:

    import lombok.experimental.UtilityClass;

    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.util.Random;

    /**
    * @Classname CaptchaUtil
    * @Description 生成验证码工具类
    * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com
    * @Date 2019-06-22 08:04
    * @Version 1.0
    */
    @UtilityClass
    public class CaptchaUtil {


    private int width = 200;
    private int height = 50;


    public BufferedImage createImage(){
    //生成对应宽高的初始图片
    return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    }

    public static String drawRandomText(BufferedImage verifyImg) {
    Graphics2D graphics = (Graphics2D) verifyImg.getGraphics();
    //设置画笔颜色-验证码背景色
    graphics.setColor(Color.WHITE);
    //填充背景
    graphics.fillRect(0, 0, width, height);
    graphics.setFont(new Font("微软雅黑", Font.PLAIN, 30));
    //数字和字母的组合
    String baseNumLetter = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
    StringBuilder sBuffer = new StringBuilder();
    //旋转原点的 x 坐标
    int x = 10;
    String ch = "";
    Random random = new Random();
    for (int i = 0; i < 4; i++) {
    graphics.setColor(getRandomColor());
    //设置字体旋转角度
    //角度小于30度
    int degree = random.nextInt() % 30;
    int dot = random.nextInt(baseNumLetter.length());
    ch = baseNumLetter.charAt(dot) + "";
    sBuffer.append(ch);
    //正向旋转
    graphics.rotate(degree * Math.PI / 180, x, 45);
    graphics.drawString(ch, x, 45);
    //反向旋转
    graphics.rotate(-degree * Math.PI / 180, x, 45);
    x += 48;
    }

    //画干扰线
    for (int i = 0; i < 6; i++) {
    // 设置随机颜色
    graphics.setColor(getRandomColor());
    // 随机画线
    graphics.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height));
    }
    //添加噪点
    for (int i = 0; i < 30; i++) {
    int x1 = random.nextInt(width);
    int y1 = random.nextInt(height);
    graphics.setColor(getRandomColor());
    graphics.fillRect(x1, y1, 2, 1);
    }
    return sBuffer.toString();
    }

    /**
    * 随机取色
    */
    private static Color getRandomColor() {
    Random ran = new Random();
    return new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));

    }
    }


    10 配置 security的权限的 注解spring security
    通过 @PreAuthorize 包含了 角色 和权限

    /**
    * 保存部门信息
    *
    * @param sysDept
    * @return
    */
    @SysLog(descrption = "保存部门信息")
    @PostMapping
    @PreAuthorize("hasAuthority('sys:dept:add')")
    public R save(@RequestBody SysDept sysDept) {
    return R.ok(deptService.save(sysDept));
    }

    /**
    * 获取部门信息
    *
    * @return
    */
    @GetMapping
    @PreAuthorize("hasAuthority('sys:dept:view')")
    public R getDeptList() {
    return R.ok(deptService.selectDeptList());
    }

    11 通过注解log的 事件监听器的方式,自动保持被注解的
    方法 生成的log保持 到数据库中,用到了自定义注解和
    数据库保持和 spring自带的 eventListener


    import com.xd.pre.domain.SysLog;
    import com.xd.pre.service.ISysLogService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.event.EventListener;
    import org.springframework.core.annotation.Order;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;

    /**
    * @Classname SysLogListener
    * @Description 注解形式的监听 异步监听日志事件
    * @Author 李号东 lihaodongmail@163.com
    * @Date 2019-04-28 11:34
    * @Version 1.0
    */
    @Slf4j
    @Component
    public class SysLogListener {

    @Autowired
    private ISysLogService sysLogService;

    @Async
    @Order
    @EventListener(SysLogEvent.class)
    public void saveSysLog(SysLogEvent event) {
    SysLog sysLog = (SysLog) event.getSource();
    // 保存日志
    sysLogService.save(sysLog);
    }
    }

    import com.xd.pre.domain.SysLog;
    import org.springframework.context.ApplicationEvent;

    /**
    * @Classname SysLogEvent
    * @Description 系统日志事件
    * @Author 李号东 lihaodongmail@163.com
    * @Date 2019-04-28 11:34
    * @Version 1.0
    */
    public class SysLogEvent extends ApplicationEvent {

    public SysLogEvent(SysLog sysLog) {
    super(sysLog);
    }
    }

    import cn.hutool.core.convert.Convert;
    import cn.hutool.core.util.URLUtil;
    import cn.hutool.extra.servlet.ServletUtil;
    import com.xd.pre.security.PreUser;
    import com.xd.pre.security.util.SecurityUtil;
    import com.xd.pre.utils.LogUtil;
    import com.xd.pre.utils.R;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;

    import javax.servlet.http.HttpServletRequest;
    import java.time.Instant;
    import java.time.LocalDateTime;
    import java.util.Arrays;
    import java.util.Objects;

    /**
    * @Classname SysLogAspect
    * @Description 系统日志切面
    * @Author 李号东 lihaodongmail@163.comgit reset --merge
    * @Date 2019-04-22 23:52
    * @Version 1.0
    * ①切面注解得到请求数据 -> ②发布监听事件 -> ③异步监听日志入库
    */
    @Slf4j
    @Aspect
    @Component
    public class SysLogAspect {


    /**
    *log实体类
    **/
    private com.xd.pre.domain.SysLog sysLog = new com.xd.pre.domain.SysLog();

    /**
    * 事件发布是由ApplicationContext对象管控的,我们发布事件前需要注入ApplicationContext对象调用publishEvent方法完成事件发布
    **/
    @Autowired
    private ApplicationContext applicationContext;

    /***
    * 定义controller切入点拦截规则,拦截SysLog注解的方法
    */
    @Pointcut("@annotation(com.xd.pre.log.SysLog)")
    public void sysLogAspect() {

    }

    /***
    * 拦截控制层的操作日志
    * @param joinPoint
    * @return
    * @throws Throwable
    */
    @Before(value = "sysLogAspect()")
    public void recordLog(JoinPoint joinPoint) throws Throwable {

    // 开始时间
    long beginTime = Instant.now().toEpochMilli();
    HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    PreUser securityUser = SecurityUtil.getUser();
    sysLog.setUserName(securityUser.getUsername());
    sysLog.setActionUrl(URLUtil.getPath(request.getRequestURI()));
    sysLog.setStartTime(LocalDateTime.now());
    sysLog.setRequestIp(ServletUtil.getClientIP(request));
    sysLog.setRequestMethod(request.getMethod());
    sysLog.setUa(request.getHeader("user-agent"));
    //访问目标方法的参数 可动态改变参数值
    Object[] args = joinPoint.getArgs();
    //获取执行的方法名
    sysLog.setActionMethod(joinPoint.getSignature().getName());
    // 类名
    sysLog.setClassPath(joinPoint.getTarget().getClass().getName());
    sysLog.setActionMethod(joinPoint.getSignature().getName());
    sysLog.setFinishTime(LocalDateTime.now());
    // 参数
    sysLog.setParams(Arrays.toString(args));
    sysLog.setDescription(LogUtil.getControllerMethodDescription(joinPoint));
    long endTime = Instant.now().toEpochMilli();
    sysLog.setConsumingTime(endTime - beginTime);
    }

    /**
    * 返回通知
    * @param ret
    * @throws Throwable
    */
    @AfterReturning(returning = "ret", pointcut = "sysLogAspect()")
    public void doAfterReturning(Object ret) {
    // 处理完请求,返回内容
    R r = Convert.convert(R.class, ret);
    if (r.getCode() == 200){
    // 正常返回
    sysLog.setType(1);
    } else {
    sysLog.setType(2);
    sysLog.setExDetail(r.getMsg());
    }
    // 发布事件
    applicationContext.publishEvent(new SysLogEvent(sysLog));
    }

    /**
    * 异常通知
    * @param e
    */
    @AfterThrowing(pointcut = "sysLogAspect()",throwing = "e")
    public void doAfterThrowable(Throwable e){
    // 异常
    sysLog.setType(2);
    // 异常对象
    sysLog.setExDetail(LogUtil.getStackTrace(e));
    // 异常信息
    sysLog.setExDesc(e.getMessage());
    // 发布事件
    applicationContext.publishEvent(new SysLogEvent(sysLog));
    }

    }

    import java.lang.annotation.*;

    @Retention(RetentionPolicy.RUNTIME)//元注解,定义注解被保留策略,一般有三种策略
    //1、RetentionPolicy.SOURCE 注解只保留在源文件中,在编译成class文件的时候被遗弃
    //2、RetentionPolicy.CLASS 注解被保留在class中,但是在jvm加载的时候北欧抛弃,这个是默认的声明周期
    //3、RetentionPolicy.RUNTIME 注解在jvm加载的时候仍被保留
    @Target({ElementType.METHOD}) //定义了注解声明在哪些元素之前
    @Documented
    public @interface SysLog {
    //定义成员
    String descrption() default "" ;//描述
    }


    用法就是

    @SysLog(descrption = "保存部门信息")
    @PostMapping
    @PreAuthorize("hasAuthority('sys:dept:add')")
    public R save(@RequestBody SysDept sysDept) {
    return R.ok(deptService.save(sysDept));
    }

    11 dto的用法

    DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。


    12 用 mybatisplus的好处


    import com.xd.pre.domain.SysLog;
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;

    /**
    * <p>
    * 系统日志 Mapper 接口
    * </p>
    *
    * @author lihaodong
    * @since 2019-04-27
    */
    public interface SysLogMapper extends BaseMapper<SysLog> {

    }


    如果时复杂的查询语句


    public interface SysMenuMapper extends BaseMapper<SysMenu> {


    @Select("select m.perms from sys_menu m, sys_user u, sys_user_role ur, sys_role_menu rm " +
    " where u.user_id = #{user_id} and u.user_id = ur.user_id " +
    " and ur.role_id = rm.role_id and rm.menu_id = m.menu_id")
    List<String> findPermsByUserId(Integer userId);

    }

    好多语法用的是注解版的 mybatis,不提倡和 data-jpa很类似

    public interface SysMenuMapper extends BaseMapper<SysMenu> {


    @Select("select m.perms from sys_menu m, sys_user u, sys_user_role ur, sys_role_menu rm " +
    " where u.user_id = #{user_id} and u.user_id = ur.user_id " +
    " and ur.role_id = rm.role_id and rm.menu_id = m.menu_id")
    List<String> findPermsByUserId(Integer userId);

    }

    12 Spring security 所有配置 共7个类,
    
    
    package com.xd.pre.security;
    
    import cn.hutool.core.util.ObjectUtil;
    import com.xd.pre.domain.SysUser;
    import com.xd.pre.service.ISysUserService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    
    import java.util.Collection;
    import java.util.Set;
    
    /**
     * @Classname UserDetailsServiceImpl
     * @Description 身份验证
     * @Author 李号东 lihaodongmail@163.com
     * @Date 2019-05-07 20:30
     * @Version 1.0
     */
    @Slf4j
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private ISysUserService userService;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            SysUser user = userService.findByUserName(username);
            if (ObjectUtil.isNull(user)) {
                log.info("登录用户:" + username + " 不存在.");
                throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
            }
            // 获取用户拥有的角色
            // 用户权限列表,根据用户拥有的权限标识与如 @PreAuthorize("hasAuthority('sys:menu:view')") 标注的接口对比,决定是否可以调用接口
            // 权限集合
            Set<String> permissions = userService.findPermsByUserId(user.getUserId());
            // 角色集合
            Set<String> roleIds = userService.findRoleIdByUserId(user.getUserId());
            permissions.addAll(roleIds);
            Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(permissions.toArray(new String[0]));
            return new PreUser(user.getUserId(), username, user.getPassword(), authorities);
        }
    }
    
    package com.xd.pre.security;
    
    import com.xd.pre.domain.SysRole;
    import com.xd.pre.domain.SysUser;
    import lombok.Getter;
    import lombok.Setter;
    import lombok.experimental.Accessors;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.util.Collection;
    import java.util.List;
    
    /**
     * @Author 李号东
     * @Description 用户身份权限认证类 登陆身份认证
     * @Date 2019-05-07 09:11
     * @Param
     * @return
     **/
    @Setter
    @Getter
    @Accessors(chain = true)
    public class PreUser implements UserDetails {
    
        private static final long serialVersionUID = 1L;
    
    
        private Integer userId;
        private String username;
        private String password;
        private Collection<? extends GrantedAuthority> authorities;
    
    
        public PreUser(Integer userId, String username, String password, Collection<? extends GrantedAuthority> authorities) {
            this.userId = userId;
            this.username = username;
            this.password = password;
            this.authorities = authorities;
    
        }
    
        public PreUser(String username, String password, List<SysRole> role) {
            this.username = username;
            this.password = password;
        }
    
        public PreUser(String username, String password) {
            this.username = username;
            this.password = password;
        }
    
        /**
         * 返回分配给用户的角色列表
         * @return
         */
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorities;
        }
    
        /**
         * 账户是否未过期,过期无法验证
         * @return
         */
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        /**
         * 指定用户是否解锁,锁定的用户无法进行身份验证
         * @return
         */
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        /**
         * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
         * @return
         */
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        /**
         * 是否可用 ,禁用的用户不能身份验证
         * @return
         */
        @Override
        public boolean isEnabled() {
            return true;
        }
    }
    
    
    package com.xd.pre.security.util;
    
    import com.alibaba.fastjson.JSON;
    import com.xd.pre.exception.BaseException;
    import com.xd.pre.security.PreUser;
    import com.xd.pre.utils.R;
    import lombok.experimental.UtilityClass;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.authentication.*;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @Classname SecurityUtil
     * @Description 安全服务工具类
     * @Author 李号东 lihaodongmail@163.com
     * @Date 2019-05-08 10:12
     * @Version 1.0
     */
    @UtilityClass
    public class SecurityUtil {
    
        /**
         * 获取用户
         *
         * @param authentication
         * @return PreUser
         * <p>
         */
        private PreUser getUser(Authentication authentication) {
            Object principal = authentication.getPrincipal();
            if (principal instanceof PreUser) {
                return (PreUser) principal;
            }
            return null;
        }
    
        public void writeJavaScript(R r, HttpServletResponse response) throws IOException {
            response.setStatus(200);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            PrintWriter printWriter = response.getWriter();
            printWriter.write(JSON.toJSONString(r));
            printWriter.flush();
        }
    
        /**
         * 获取Authentication
         */
        private Authentication getAuthentication() {
            return SecurityContextHolder.getContext().getAuthentication();
        }
    
        /**
         * @Author 李号东
         * @Description 获取用户
         * @Date 11:29 2019-05-10
         **/
        public PreUser getUser(){
            try {
                return (PreUser) getAuthentication().getPrincipal();
            } catch (Exception e) {
                throw new BaseException("登录状态过期", HttpStatus.UNAUTHORIZED.value());
            }
        }
    }
    
    
    package com.xd.pre.security.util;
    
    import com.xd.pre.security.PreUser;
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import lombok.extern.log4j.Log4j2;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.*;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.stereotype.Component;
    
    /**
     * @Classname JwtUtil
     * @Description JWT工具类
     * @Author 李号东 lihaodongmail@163.com
     * @Date 2019-05-07 09:23
     * @Version 1.0
     */
    @Log4j2
    @Component
    public class JwtUtil {
    
        /**
         * 用户名称
         */
        private static final String USERNAME = Claims.SUBJECT;
    
        private static final String USERID = "userid";
        /**
         * 创建时间
         */
        private static final String CREATED = "created";
        /**
         * 权限列表
         */
        private static final String AUTHORITIES = "authorities";
        /**
         * 密钥
         */
        private static final String SECRET = "abcdefgh";
        /**
         * 有效期1小时
         */
        private static final long EXPIRE_TIME = 60 * 60 * 1000;
    
        @Value("${jwt.header}")
        private String tokenHeader;
    
        @Value("${jwt.tokenHead}")
        private String authTokenStart;
    
        /**
         * 生成令牌
         *
         * @return 令牌
         */
        public static String generateToken(PreUser userDetail) {
            Map<String, Object> claims = new HashMap<>(3);
            claims.put(USERID,userDetail.getUserId());
            claims.put(USERNAME, userDetail.getUsername());
            claims.put(CREATED, new Date());
            claims.put(AUTHORITIES, userDetail.getAuthorities());
            return generateToken(claims);
        }
    
        /**
         * 从数据声明生成令牌
         *
         * @param claims 数据声明
         * @return 令牌
         */
        private static String generateToken(Map<String, Object> claims) {
            Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET).compact();
        }
    
        /**
         * 从令牌中获取用户名
         *
         * @param token 令牌
         * @return 用户名
         */
        public static String getUsernameFromToken(String token) {
            Claims claims = getClaimsFromToken(token);
            return claims.getSubject();
        }
    
        /**
         * 根据请求令牌获取登录认证信息
         *
         * @return 用户名
         */
        public PreUser getUserFromToken(HttpServletRequest request) {
            // 获取请求携带的令牌
            String token = getToken(request);
            if (StringUtils.isNotEmpty(token)) {
                Claims claims = getClaimsFromToken(token);
                if (claims == null) {
                    return null;
                }
                String username = claims.getSubject();
                if (username == null) {
                    return null;
                }
                if (isTokenExpired(token)) {
                    return null;
                }
                // 解析对应的权限以及用户id
                Object authors = claims.get(AUTHORITIES);
                Integer userId = (Integer)claims.get(USERID);
                Set<String> perms = new HashSet<>();
                if (authors instanceof List) {
                    for (Object object : (List) authors) {
                        perms.add(((Map) object).get("authority").toString());
                    }
                }
                Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(perms.toArray(new String[0]));
                if (validateToken(token, username)){
                    // 未把密码放到jwt
                    return new PreUser(userId,username,"",authorities);
                }
            }
            return null;
        }
    
        /**
         * 从令牌中获取数据声明
         *
         * @param token 令牌
         * @return 数据声明
         */
        private static Claims getClaimsFromToken(String token) {
            Claims claims;
            try {
                claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
            } catch (Exception e) {
                claims = null;
            }
            return claims;
        }
    
        /**
         * 验证令牌
         *
         * @param token
         * @param username
         * @return
         */
        private static Boolean validateToken(String token, String username) {
            String userName = getUsernameFromToken(token);
            return (userName.equals(username) && !isTokenExpired(token));
        }
    
        /**
         * 刷新令牌
         *
         * @param token
         * @return
         */
        public static String refreshToken(String token) {
            String refreshedToken;
            try {
                Claims claims = getClaimsFromToken(token);
                claims.put(CREATED, new Date());
                refreshedToken = generateToken(claims);
            } catch (Exception e) {
                refreshedToken = null;
            }
            return refreshedToken;
        }
    
        /**
         * 判断令牌是否过期
         *
         * @param token 令牌
         * @return 是否过期
         */
        private static Boolean isTokenExpired(String token) {
            try {
                Claims claims = getClaimsFromToken(token);
                Date expiration = claims.getExpiration();
                return expiration.before(new Date());
            } catch (Exception e) {
                return false;
            }
        }
    
        /**
         * 获取请求token
         *
         * @param request
         * @return
         */
        private String getToken(HttpServletRequest request) {
            String token = request.getHeader(tokenHeader);
            if (StringUtils.isNotEmpty(token)) {
                token = token.substring(authTokenStart.length());
            }
            return token;
        }
    
    
    }
    
    
    package com.xd.pre.security.handle;
    
    import cn.hutool.http.Status;
    import com.xd.pre.security.util.SecurityUtil;
    import com.xd.pre.utils.R;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.Serializable;
    
    /**
     * 认证失败处理类 返回401
     * @author lihaodong
     */
    @Slf4j
    @Component
    public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {
    
        private static final long serialVersionUID = -8970718410437077606L;
    
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
            log.error("请求访问: " + request.getRequestURI() + " 接口, 经jwt认证失败,无法访问系统资源.");
            SecurityUtil.writeJavaScript(R.error(Status.HTTP_UNAUTHORIZED,"请求访问:" + request.getRequestURI() + "接口,经jwt 认证失败,无法访问系统资源"),response);
        }
    }
    
    package com.xd.pre.security.filter;
    
    import cn.hutool.core.util.ObjectUtil;
    import com.xd.pre.security.PreUser;
    import com.xd.pre.security.util.JwtUtil;
    import com.xd.pre.service.ISysUserService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Collection;
    import java.util.Set;
    
    /**
     * @Author 李号东
     * @Description token过滤器来验证token有效性 引用的stackoverflow一个答案里的处理方式
     * @Date 00:32 2019-03-17
     * @Param
     * @return
     **/
    @Slf4j
    @Component
    public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    
        @Autowired
        private JwtUtil jwtUtil;
    
        @Autowired
        private ISysUserService userService;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
    
            PreUser securityUser = jwtUtil.getUserFromToken(request);
            if (ObjectUtil.isNotNull(securityUser)){
                Set<String> permissions = userService.findPermsByUserId(securityUser.getUserId());
                Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(permissions.toArray(new String[0]));
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(securityUser, null, authorities);
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
            chain.doFilter(request, response);
        }
    }
    
    package com.xd.pre.security.config;
    
    import com.xd.pre.security.UserDetailsServiceImpl;
    import com.xd.pre.security.filter.JwtAuthenticationTokenFilter;
    import com.xd.pre.security.handle.AuthenticationEntryPointImpl;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    /**
     * @Classname WebSecurityConfig
     * @Description Security配置类
     * @Author 李号东 lihaodongmail@163.com
     * @Date 2019-05-07 09:10
     * @Version 1.0
     */
    @Slf4j
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private AuthenticationEntryPointImpl unauthorizedHandler;
    
        @Autowired
        private JwtAuthenticationTokenFilter authenticationTokenFilter;
    
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
    
    
        /**
         * 解决 无法直接注入 AuthenticationManager
         *
         * @return
         * @throws Exception
         */
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        /**
         * 配置策略
         *
         * @param httpSecurity
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
    
            httpSecurity
                    // 由于使用的是JWT,我们这里不需要csrf
                    .csrf().disable()
                    // 认证失败处理类
                    .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                    // 基于token,所以不需要session
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                    // 过滤请求
                    .authorizeRequests()
                    // 对于登录login 图标 要允许匿名访问
                    .antMatchers("/login/**", "/favicon.ico").anonymous()
                    .antMatchers(
                            HttpMethod.GET,
                            "/*.html",
                            "/**/*.html",
                            "/**/*.css",
                            "/**/*.js"
                    ).permitAll()
                    // swagger start
                    .antMatchers("/swagger-ui.html").anonymous()
                    .antMatchers("/swagger-resources/**").anonymous()
                    .antMatchers("/webjars/**").anonymous()
                    .antMatchers("/*/api-docs").anonymous()
                    // swagger end
                    .antMatchers("/captcha.jpg")
                    .permitAll()
                    // 访问/user 需要拥有admin权限
                    //  .antMatchers("/user").hasAuthority("ROLE_ADMIN")
                    // 除上面外的所有请求全部需要鉴权认证
                    .anyRequest().authenticated()
                    .and()
                    .headers().frameOptions().disable();
            // 添加JWT filter
            httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        }
    
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth
                    // 设置UserDetailsService
                    .userDetailsService(userDetailsService)
                    // 使用BCrypt进行密码的hash
                    .passwordEncoder(passwordEncoder());
        }
    
        /**
         * 装载BCrypt密码编码器 密码加密
         *
         * @return
         */
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }


    13 果然事务全部加在了 service层了


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeById(Serializable id) {
    // 部门层级删除
    List<Integer> idList = this.list(Wrappers.<SysDept>query().lambda().eq(SysDept::getParentId, id)).stream().map(SysDept::getDeptId).collect(Collectors.toList());
    // 删除自己
    idList.add((Integer) id);
    return super.removeByIds(idList);
    }

    14 list 迭代器的用法

    13 果然事务全部加在了 service层了
    
    
        @Transactional(rollbackFor = Exception.class)
        @Override
        public boolean removeById(Serializable id) {
            // 部门层级删除
            List<Integer> idList = this.list(Wrappers.<SysDept>query().lambda().eq(SysDept::getParentId, id)).stream().map(SysDept::getDeptId).collect(Collectors.toList());
            // 删除自己
            idList.add((Integer) id);
            return super.removeByIds(idList);
        }
    
    14 list 迭代器的用法
    
         */
        private SysDept getDepartment(Integer deptId) {
            List<SysDept> departments = baseMapper.selectList(Wrappers.<SysDept>query().select("dept_id", "name", "parent_id", "sort", "create_time"));
            Map<Integer, SysDept> map = departments.stream().collect(
                    Collectors.toMap(SysDept::getDeptId, department -> department));
    
            for (SysDept dept : map.values()) {
                SysDept parent = map.get(dept.getParentId());
                if (parent != null) {
                    List<SysDept> children = parent.getChildren() == null ? new ArrayList<>() : parent.getChildren();
                    children.add(dept);
                    parent.setChildren(children);
                }
            }
            return map.get(deptId);
        }

     15 双冒号的用法

    List<String> a1 = Arrays.asList("a", "b", "c");
    public static void printValur(String str) {
    System.out.println("print value : " + str);
    }
    使用前
    a:
    for (String a : a1) {
    printValur(a);
    };
    b:
    a1.forEach(x -> MyTest.printValur(x));
    使用后
    a1.forEach(MyTest::printValur);

    return sysDepts.stream().map(SysDept::getDeptId).collect(Collectors.toList());

  • 相关阅读:
    电脑技巧1
    web前端学习网站汇总1
    11月20日学习日志
    11月16日学习日志
    11月18日学习日志
    11月13日学习日志
    11月12日学习日志
    11月17日学习日志
    11月15日学习日志
    11月11日学习日志
  • 原文地址:https://www.cnblogs.com/genestart/p/11286371.html
Copyright © 2011-2022 走看看