Springboot开发一个小而美的博客(四)

4 设计与规范

4.1 实体设计
实体类

  • 博客Blog
  • 博客分类Type
  • 博客标签Tag
  • 博客评论Comment
  • 用户User

5 后台管理

5.1 登录

1 构建登录页面和后台管理首页
2 UserService和UserDao
3 LoginController实现登录
4 MD5加密
5 登录拦截器

登录注销操作

@Controller
@RequestMapping("/admin")
public class LoginController {

    @Autowired
    private UserService userService;

    //管理员登录的入口(跳转到登录页面)
    @GetMapping()
    public String loginPage(){
        return "admin/login";
    }

    @PostMapping("/login")
    public String login(@RequestParam String username,
                        @RequestParam String password,
                        HttpSession session,
                        RedirectAttributes attributes){
        User user = userService.checkUser(username, password);
        if(user != null){
//            浏览器获取不到密码
            user.setPassword(null);
            session.setAttribute("user", user);
            return "admin/index";
        }else {

//            数据重定向需要用RedirectAttributes,用Model获取不到
            attributes.addFlashAttribute("msg", "用户名或密码错误");
            return "redirect:/admin";
        }
    }

    @GetMapping("/logout")
    public String logout(HttpSession session){
//        注销清空session域中信息
        session.removeAttribute("user");
        return "redirect:/admin";
    }

加密类

public class MD5Utils {

    /**
     * MD5加密类
     * @param str 要加密的字符串
     * @return    加密后的字符串
     */
    public static String code(String str){
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte[]byteDigest = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < byteDigest.length; offset++) {
                i = byteDigest[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            //32位加密
            return buf.toString();
            // 16位的加密
            //return buf.toString().substring(8, 24);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }

    }

在service层将密码加密后再与数据库中已加密的密码比较

    @Override
    public User checkUser(String username, String password) {
        User user = userDao.queryByUsernameAndPassword(username, MD5Utils.code(password));
        return user;
    }

加密的意义避免数据库存储明文用户密码,避免数据库泄露后造成用户密码泄露

拦截器类

//登录拦截器
//extends HandlerInterceptAdapter 已过时
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        if (request.getSession().getAttribute("user") == null){
            response.sendRedirect("/admin");
            return false;
        }
        return true;
    }
}

配置拦截器配置类

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册自定义拦截器
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/admin/**")
//                不能拦截的路径
                .excludePathPatterns("/admin")
                .excludePathPatterns("/admin/login");
    }
}

5.2 分类管理

  1. 分类管理页面

在这里插入图片描述
2. 分类列表分页

添加pagehelper依赖

<!--    pagehelper   -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>

通过导入pagehelper的pageInfo对象
将查询到的数据进行分页(属于逻辑分页)


  @GetMapping("/types")
    public String types(@RequestParam(required = false,defaultValue = "1",value = "pageNum")int pageNum,
                        Model model){
        Integer p = (Integer) model.getAttribute("pageNum");
        if (p!=null){
            pageNum = p;
        }
        PageHelper.startPage(pageNum, 5);
        List<Type> allType = typeService.getAllType();
        //得到分页结果对象
        PageInfo<Type> pageInfo = new PageInfo<>(allType);
        model.addAttribute("pageInfo", pageInfo);
        return "admin/types";
    }

pageInfo有很多属性方法可以满足基本分页功能需求

         <div class="ui success message" th:unless="${#strings.isEmpty(msg)}">
            <i class="close icon"></i>
            <div class="header">提示:</div>
            <p th:text="${msg}">恭喜,操作成功!</p>
          </div>

          <tr th:each="type, iterStat : ${pageInfo.list}">
            <td th:text="${iterStat.count}"></td>
            <td th:text="${type.name}"></td>
            <td>
              <a href="#" th:href="@{/admin/types/{id}/input(id=${type.id})}" class="ui mini teal basic button">编辑</a>
              <a href="#" th:href="@{/admin/types/{id}/{pageNum}/delete(id=${type.id},pageNum=${pageInfo.pageNum})}" class="ui mini red basic button">删除</a>
            </td>
          </tr>
          </tbody>
          <tfoot>
          <tr>
            <th colspan="7">
              <div class="ui mini pagination menu"  th:if="${pageInfo.pages}">
                <a th:href="@{/admin/types}" th:unless="${pageInfo.isIsFirstPage()}" class="item">首页</a>
                <a th:href="@{/admin/types(pageNum=${pageInfo.hasPreviousPage}?${pageInfo.prePage}:1)}" th:unless="!${pageInfo.isHasPreviousPage()}" class="item">上一页</a>
                <a th:href="@{/admin/types(pageNum=${pageInfo.hasNextPage}?${pageInfo.nextPage}:${pageInfo.pages})}" th:unless="!${pageInfo.isHasNextPage()} " class="item">下一页</a>
                <a th:href="@{/admin/types(pageNum=${pageInfo.pages})}" th:unless="${pageInfo.isIsLastPage()}" class="item">尾页</a>
              </div>
              <a href="#" th:href="@{/admin/types/input}" class="ui mini right floated teal basic button">新增</a>
            </th>
          </tr>
          </tfoot>
  1. 分类新增,修改,删除

新增删除,修改,删除操作较简单,在这里可以省略

主要注意点
如何实现一个页面完成增加修改操作

    @GetMapping("/types/input")
    public String toAddType(Model model){
        model.addAttribute("type", new Type());   //返回一个type对象给前端th:object
        return "admin/types-input";
    }

    @GetMapping("/types/{id}/input")
    public String toEditType(@PathVariable Long id,
                             Model model){
        model.addAttribute("type", typeService.getType(id));
        return "admin/types-input";
    }

通过返回一个空对象和一个从数据库获取的对象
由前端判断,对象为空?新增:修改

 <form action="#" th:object="${type}" th:action="*{id}==null ? @{/admin/types} : @{/admin/types/{id}(id=*{id})}" 
            th:method="post" class="ui form" >

如何设计url与对应映射的controller地址

举个例子

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

理解并设计rest/restful风格接口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值