登录功能+事务管理

登录功能:

Controller:
@Autowired
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
    log.info("员工登录: {}",emp);
    Emp e = empService.login(emp);
    return e != null?Result.success():Result.error("用户名或密码错误");
}

ServiceImpl:
public Emp login(Emp emp){
    return empMapper.getByUsernameAndPassword(emp);
}

Mapper:
@Select("selelct * from emp where username = #{username} and password = #{password}")
Emp getByUsernameAndPassword(Emp emp);
登陆标记:

用户登陆成功之后,每一次请求中,都可以获取到该标记

统一拦截:
  1. 过滤器Filter (servlet规范)
  2. 拦截器Interceptor (spring提供)
会话技术:
  • 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应
  • 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
会话跟踪方案:
  1. 客户端会话跟踪技术:Cookie
  2. 服务端会话跟踪技术:Session
  3. 令牌技术
方案对比:
  1. Cookie:

    优点:HTTP协议中支持的技术

    缺点:

    1. 移动端APP无法使用Cookie
    2. 不安全,用户可以自己禁用Cookie
    3. Cookie不能跨域

跨域区分为三个维度:协议、IP/域名、端口;只要三个维度中有任意维度不同,那就是跨域操作

            //设置Cookie
            @GetMapping("/c1")
            public Result cookie1(HttpServletResponse response){
                response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
                return Result.success();
            }

            //获取Cookie
            @GetMapping("/c2")
            public Result cookie2(HttpServletRequest request){
                Cookie[] cookies = request.getCookies();
                for (Cookie cookie : cookies) {
                    if(cookie.getName().equals("login_username")){
                        System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie
                    }
                }
                return Result.success();
            }
  1. Session:

    优点:存储在服务端,安全

    缺点:

    1. 服务器集群环境下无法直接使用Session
    2. Cookie的缺点
  2. 令牌技术:

    优点:

    1. 支持PC端、移动端
    2. 解决集群环境下的认证问题
    3. 减轻服务器存储压力

    缺点:

    需要自己实现

JWT:

全称:JSON Web Token(http://jwt.io/)

定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的

组成:
  • 第一部分(base64编码):Header(头),记录令牌类型、签名算法等。例如:{“alg”:”HS256″,”type”:”JWT”}
    • 对json字符串进行base64编码

    • base64编码:是一种基于64个可打印字符(A-Z,a-z,0-9,+,/)来表示二进制数据的编码方式

  • 第二部分(base64编码):Payload(有效载荷),携带一些自定义信息、默认信息等。例如:{“id”:”1″,”username”:”Tom”}
  • 第三部分(不是base64 ):Signatrue(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定密钥,通过指定签名算法计算而来,根据前面的内容自动计算而来,并不是base64

    引入依赖:



    io.jsonwebtoken
    jjwt
    0.9.1

    生成JWTS:
    —————————————————–
    @Test
    public void testGenJwt(){
    Map<String, Object> claims = new HashMap<>();
    claims.put(“id”,1);
    claims.put(“name”,”tom”);

        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "itheima")//(签名算法,"密钥")
                .setClaims(claims) //自定义内容(载荷)
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效期为1h
                .compact();//调用compact()之后会拿到一个字符串类型的返回值,返回值存到jwt
        System.out.println(jwt);
    

    }

    解析JWTS:
    —————————————————–
    public void testParseJwt(){
    Claims claims = Jwts.parser()
    .setSigningKey(“itheima”)//指定签名密钥
    //解析令牌
    .parseClaimsJws(“eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY3MDQ2NDU0N30.yPLRyiusrlrmWeC4-dhInjFuAghPkmiHSRHd_DTKi9E”)
    .getBody();
    System.out.println(claims);
    }
    —————————————————–

注意事项:
  • JWT校验时使用的签名密钥,必须和生成JWT令牌时使用的密钥的配套的
  • 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法

过滤器(Filter):

概念:Filter过滤器,是javaWeb三大组件(Servlet、Filter、Listener)之一。

  • 过滤器可以把资源的请求拦截下来,从而实现一些特殊的功能
  • 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等
快速入门:
  1. 定义Filter:定义一个类:实现Filter接口,并重写其所有方法
  2. 配置Filter:Filter类加上@WebFilter注解,配置拦截资源的路径。启动类上加@ServletComponentScan开启Servlet组件支持
    @WebFilter(urlPatterns = "/*")
    public class DemoFilter implements Filter {
        @Override //初始化方法, Web服务器启动,创建Filter时调用,只调用一次
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("init 初始化方法执行了");
        }
    
        @Override //拦截到请求之后调用, 可调用多次
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("Demo 拦截到了请求...放行前逻辑");
            //放行
            chain.doFilter(request,response);
    
            System.out.println("Demo 拦截到了请求...放行后逻辑");
        }
    
        @Override //销毁方法, 服务器关闭时调用,只调用一次
        public void destroy() {
            System.out.println("destroy 销毁方法执行了");
        }
    }
    
注意:
  1. 重写实现方法时,idea 默认只实现 doFilter,因为init和destroy方法并不常用,Filter接口中已经提供默认实现
  2. 放行后访问对应资源,资源访问完成后,还会回到Filter中
  3. 回到Filter中,执行放行之后的逻辑
Filter拦截路径:
  1. 拦截具体路径:” /login “,只有访问 /login 路径时,才会被拦截

  2. 目录拦截: ” /emps/* “,访问 /emps 下的所有资源,都会被拦截

  3. 拦截所有:” * “,访问所有资源,都会被拦截

过滤器链:

一个Web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链

顺序:注解配置的Filter,优先级是按照过滤器类名(子字符串)的自然排序

登录校验Filter:

​ 1、获取请求url

​ 2、判断请求url中是否包含login,如果包含,说明是登录操作,放行

​ 3、获取请求头中的令牌(token)

​ 4、判断令牌是否存在,如果不存在,返回错误结果(未登录)

​ 5、解析token,如果解析失败,返回错误结果(未登录)

​ 6、放行

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        //1.获取请求url。
        String url = req.getRequestURL().toString();
        log.info("请求的url: {}",url);

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){
            log.info("登录操作, 放行...");
            chain.doFilter(request,response);
            return;
        }

        //3.获取请求头中的令牌(token)。
        String jwt = req.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

        //5.解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

        //6.放行。
        log.info("令牌合法, 放行");
        chain.doFilter(request, response);

    }
}

拦截器(Interceptor):

概念:

是一种动态截取方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行

作用:

拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码

Interceptor快速入门:
  1. 定义拦截器,实现HandlerIntercrptor接口,并重写其所有方法
  2. 注册拦截器
    @Component
    public class LoginCheckInterceptor implements HandlerInterceptor {
        @Override //目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行
        public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
            System.out.print("preHandle...");
            return true;
    
        @Override //目标资源方法运行后运行
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle ...");
        }
    
        @Override //视图渲染完毕后运行, 最后运行
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion...");
        }
    }           
    -------------------------------------------------------
    @Configuration //配置类
    public class WebConfig implements WebMvcConfigurer {
    
        @Autowired
        private LoginCheckInterceptor loginCheckInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //配置拦截路径, "/**" 表示拦截所有
            registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
        }
    }
    
拦截器-拦截路径:
  • 拦截器可以根据需求,配置不同的拦截路径:
  1. ” /* “:一级路径,能匹配/depts,/emps,/login,不能匹配/depts/1
  2. ” /** “:任意级路径,能匹配depts,/depts/1,depts/1/2
  3. ” /depts/* “:/depts 下的一级路径,能匹配 /depts/1,不能匹配 /depts/1/2, /depts
  4. ” /depts/** “: /depts下的任意级路径,能匹配/depts,/depts/1,depts/1/2,不能匹配/emps/1
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置拦截路径
        // addPathPatterns("/**")表示拦截所有
        // excludePathPatterns("/login") 表示不需要拦截哪些资源
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
    }       
    
拦截器-执行流程:

​ 1、浏览器->Filter->DispatcherServlet->Interceptor->Controller

​ 2、浏览器<-Filter<-DispatcherServlet<-Interceptor<-Controller

Filter 与 Interceptor:

  1. 接口规范不同:
    1. 过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口
  2. 拦截范围不同:
    1. 过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源

登录校验Interceptor:

​ 1、获取请求url

​ 2、判断请求url中是否包含login,如果包含,说明是登录操作,放行

​ 3、获取请求头中的令牌(token)

​ 4、判断令牌是否存在,如果不存在,返回错误结果(未登录)

​ 5、解析token,如果解析失败,返回错误结果(未登录)

​ 6、放行

@Override //目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
//1.获取请求url。
String url = req.getRequestURL().toString();
log.info(“请求的url: {}”,url);

    //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
    if(url.contains("login")){
        log.info("登录操作, 放行...");
        return true;
    }

    //3.获取请求头中的令牌(token)。
    String jwt = req.getHeader("token");

    //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
    if(!StringUtils.hasLength(jwt)){
        log.info("请求头token为空,返回未登录的信息");
        Result error = Result.error("NOT_LOGIN");
        //手动转换 对象--json --------> 阿里巴巴fastJSON
        String notLogin = JSONObject.toJSONString(error);
        resp.getWriter().write(notLogin);
        return false;
    }

    //5.解析token,如果解析失败,返回错误结果(未登录)。
    try {
        JwtUtils.parseJWT(jwt);
    } catch (Exception e) {//jwt解析失败
        e.printStackTrace();
        log.info("解析令牌失败, 返回未登录错误信息");
        Result error = Result.error("NOT_LOGIN");
        //手动转换 对象--json --------> 阿里巴巴fastJSON
        String notLogin = JSONObject.toJSONString(error);
        resp.getWriter().write(notLogin);
        return false;
    }

    //6.放行。
    log.info("令牌合法, 放行");
    return true;
}

异常处理:

  • 方案一:在Controller的每一个方法中进行 try…catch 处理,代码臃肿,不推荐
  • 方案二:全局异常处理器,简单,优雅,推荐

抛异常:Mapper->Service->Controller->全局异常处理器

    //@RestControllerAdvice = @ControllerAdvice + @ResponseBody
    @RestControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(Exception.class)//捕获所有异常
        public Result ex(Exception ex){
            ex.printStackTrace();
            return Result.error("对不起,操作失败,请联系管理员");
        }
    }

@RestControllerAdvice 加在类上;

@ExceptionHandler 加在方法上,通过这个注解指定当前方法要捕获哪一类型的异常

事务管理:

概念:

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败

操作:

​ 1、开启事务(一组操作开始前,开启事务):start transaction / begin ;

​ 2、提交事务(这组操作全部成功后,提交事务):commit ;

​ 3、回滚事务(中间任何一个操作出现异常,回滚事务):rollback ;

例:

解散部门:删除部门,同时删除该部门下的员工

    yml:
    #spring事务管理日志
    logging:
      level:
        org.springframework.jdbc.support.JdbcTransactionManager: debug

    Controller:
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) throws Exception {
        log.info("根据id删除部门:{}",id);
        //调用service删除部门
        deptService.delete(id);
        return Result.success();
    }

    ServiceImpl:
    @Transactional
    @Autowired
    private EmpMapper empMapper;
    @Override
    public void delete(Integer id) throws Exception {
    deptMapper.deleteById(id); //根据ID删除部门数据
    empMapper.deleteByDeptId(id); //根据部门ID删除该部门下的员工
    }

    Mapper:
    //根据部门ID删除该部门下的员工数据
    @Delete("delete  from emp where dept_id = #{deptId}")
    void deleteByDeptId(Integer deptId);

Spring事务管理:

注解:@transaction

位置:业务(service)层的方法上、类上、接口上

作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务

rollbackFor:

默认情况下,只有出现RuntimeException才回滚异常。 rollvackFor 属性用于控制出现何种异常类型,回滚事务

@Transactional(rollvackFor = Exception.class)//表示所有异常都会回滚

propagation:

事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制

​ *REQUIRED:默认值,需要事务,有则加入,无则创建新事务

​ *REQUIRED_NEW:需要新事务,无论有无,总是创建新事务

​ SUPPORTS:支持事务,有则加入,无则在无事务状态中运行

​ NOT_SUPPORTED:不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务

​ MANDATORY:必须有事务,否则抛异常

​ NEVER:必须没事务,否则抛异常

例:

解散部门时,无论是成功还是失败,都要记录操作日志

步骤:

​ 1、解散部门:删除部门、删除部门下的员工

​ 2、记录日志到数据库表中

        DeptServiceImpl:
        @Autowired
        private DeptLogService deptLogService;          
        @Transactional
        @Override
        public void delete(Integer id) throws Exception {
            try {
                deptMapper.deleteById(id); //根据ID删除部门数据

                int i = 1/0;
                //if(true){throw new Exception("出错啦...");}

                empMapper.deleteByDeptId(id); //根据部门ID删除该部门下的员工
            } finally {
                DeptLog deptLog = new DeptLog();
                deptLog.setCreateTime(LocalDateTime.now());
                deptLog.setDescription("执行了解散部门的操作,此次解散的是"+id+"号部门");
                deptLogService.insert(deptLog);
                /*
                public interface DeptLogService {
                    void insert(DeptLog deptLog);
                }
                */
            }
        }

        DeptLogServiceImpl:
        @Service
        public class DeptLogServiceImpl implements DeptLogService {
            @Autowired
            private DeptLogMapper deptLogMapper;
            /*
                @Mapper
                public interface DeptLogMapper {
                    @Insert("insert into dept_log(create_time,description) values(#{createTime},#{description})")
                    void insert(DeptLog log);
                }
            */
            @Transactional(propagation = Propagation.REQUIRES_NEW)  //需要新事物,无论有无,总是创建新事物
            @Override
            public void insert(DeptLog deptLog) {
                deptLogMapper.insert(deptLog);
            }
        }   
创作不易!转载请注明作者及文章链接或作者博客链接——
- 作者:pidanxia
- 链接:https://pidanxia.ink
(链接可为:**文章链接**或者**作者博客链接**)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
新春
快乐