Browse Source

第一次提交

laowo 4 years ago
commit
217fff10a0
29 changed files with 1190 additions and 0 deletions
  1. 91 0
      pom.xml
  2. 15 0
      src/main/java/com/itheima/ShiroPermApp.java
  3. 36 0
      src/main/java/com/itheima/controller/AdminController.java
  4. 43 0
      src/main/java/com/itheima/controller/LoginController.java
  5. 23 0
      src/main/java/com/itheima/controller/PageController.java
  6. 28 0
      src/main/java/com/itheima/controller/StorageController.java
  7. 82 0
      src/main/java/com/itheima/exception/GloableExceptionResolver.java
  8. 26 0
      src/main/java/com/itheima/mapper/AdministratorMapper.java
  9. 10 0
      src/main/java/com/itheima/mapper/StorageMapper.java
  10. 39 0
      src/main/java/com/itheima/pojo/Administrator.java
  11. 16 0
      src/main/java/com/itheima/pojo/Permission.java
  12. 19 0
      src/main/java/com/itheima/pojo/Role.java
  13. 16 0
      src/main/java/com/itheima/pojo/Storage.java
  14. 18 0
      src/main/java/com/itheima/service/AdminService.java
  15. 13 0
      src/main/java/com/itheima/service/StorageService.java
  16. 70 0
      src/main/java/com/itheima/service/impl/AdminServiceImpl.java
  17. 23 0
      src/main/java/com/itheima/service/impl/StorageServiceImpl.java
  18. 60 0
      src/main/java/com/itheima/shiroConfig/MyRealm.java
  19. 25 0
      src/main/java/com/itheima/shiroConfig/MyRedisCacheManager.java
  20. 141 0
      src/main/java/com/itheima/shiroConfig/ShiroConfig.java
  21. 227 0
      src/main/java/com/itheima/shiroConfig/ShiroRedisCache.java
  22. 69 0
      src/main/java/com/itheima/shiroConfig/ShiroRedisSessionDao.java
  23. 12 0
      src/main/resources/application.yml
  24. 1 0
      src/main/resources/static/js/jquery.js
  25. 10 0
      src/main/resources/templates/denied.html
  26. 14 0
      src/main/resources/templates/index.html
  27. 15 0
      src/main/resources/templates/login.html
  28. 16 0
      src/main/resources/templates/success.html
  29. 32 0
      src/test/java/com/itheima/test/DAOTest.java

+ 91 - 0
pom.xml

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.itheima</groupId>
+    <artifactId>springboot_shiro_demo</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.2.6.RELEASE</version>
+    </parent>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+
+        <mybatisplus-starter.version>1.0.5</mybatisplus-starter.version>
+        <mybatisplus.version>2.2.0</mybatisplus.version>
+    </properties>
+
+    <dependencies>
+        <!--引用整合依赖,本案例不使用starter依赖-->
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-spring</artifactId>
+            <version>1.5.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- MP的相关依赖 -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatisplus-spring-boot-starter</artifactId>
+            <version>${mybatisplus-starter.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus</artifactId>
+            <version>${mybatisplus.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <!--<version>5.1.36</version>-->
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <!--thymeleaf相关依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.theborakompanioni</groupId>
+            <artifactId>thymeleaf-extras-shiro</artifactId>
+            <version>2.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 15 - 0
src/main/java/com/itheima/ShiroPermApp.java

@@ -0,0 +1,15 @@
+package com.itheima;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+@SpringBootApplication
+@EnableTransactionManagement
+@MapperScan("com.itheima.mapper")
+public class ShiroPermApp {
+    public static void main(String[] args) {
+        SpringApplication.run(ShiroPermApp.class, args);
+    }
+}

+ 36 - 0
src/main/java/com/itheima/controller/AdminController.java

@@ -0,0 +1,36 @@
+package com.itheima.controller;
+
+
+import com.itheima.pojo.Administrator;
+import com.itheima.service.AdminService;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ *
+ */
+@RestController
+@RequestMapping("/user")
+public class AdminController {
+
+    @Autowired
+    private AdminService adminService;
+
+    @RequestMapping("/all")
+    public List<Administrator> findAll(){
+        return adminService.findAll();
+    }
+
+    @RequestMapping("/del/{id}")
+    @RequiresRoles("role_superman")
+    public String delAdmin(@PathVariable("id")Integer id){
+
+//        SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal() //用户
+        adminService.deleteById(id);
+        return "OK";
+    }
+}

+ 43 - 0
src/main/java/com/itheima/controller/LoginController.java

@@ -0,0 +1,43 @@
+package com.itheima.controller;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.IncorrectCredentialsException;
+import org.apache.shiro.authc.UnknownAccountException;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.subject.Subject;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ *
+ */
+@Controller
+@RequestMapping("/backend")
+public class LoginController {
+
+    @RequestMapping("login")
+    public String login(@RequestParam String username,@RequestParam String password,Model model){
+        try{
+            // subject - securityManager - realm
+            Subject subject = SecurityUtils.getSubject();
+            AuthenticationToken token = new UsernamePasswordToken(username,password);
+            subject.login(token);
+            return "success";
+        }catch (UnknownAccountException e){
+            e.printStackTrace();
+            model.addAttribute("errMessage","用户不存在");
+            return "login";
+        }catch (IncorrectCredentialsException e){
+            e.printStackTrace();
+            model.addAttribute("errMessage","密码错误");
+            return "login";
+        }catch (Exception e){
+            e.printStackTrace();
+            model.addAttribute("errMessage","系统错误");
+            return "login";
+        }
+    }
+}

+ 23 - 0
src/main/java/com/itheima/controller/PageController.java

@@ -0,0 +1,23 @@
+package com.itheima.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * thymeleaf页面跳转
+ */
+@Controller
+@RequestMapping("/page")
+public class PageController {
+
+    @RequestMapping("toLogin")
+    public String toLogin(Model model){
+        model.addAttribute("errMessage","开始登陆");
+        return "login"; //转发到classpath下面的templates下面的名为login 后缀html的
+    }
+    @RequestMapping("toDenied")
+    public String toDenied(){
+        return "denied";
+    }
+}

+ 28 - 0
src/main/java/com/itheima/controller/StorageController.java

@@ -0,0 +1,28 @@
+package com.itheima.controller;
+
+
+import com.itheima.pojo.Storage;
+import com.itheima.service.StorageService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ *
+ */
+@RestController
+@RequestMapping("/storage")
+public class StorageController {
+
+    @Autowired
+    private StorageService storageService;
+
+
+    @RequestMapping("all")
+    public List<Storage> findAll(){
+        return storageService.findAll();
+    }
+}

+ 82 - 0
src/main/java/com/itheima/exception/GloableExceptionResolver.java

@@ -0,0 +1,82 @@
+package com.itheima.exception;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.UnauthenticatedException;
+import org.apache.shiro.authz.UnauthorizedException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ *
+ */
+@ControllerAdvice
+public class GloableExceptionResolver {
+
+    @ExceptionHandler(UnauthorizedException.class)
+    public void calUnauthorizedException(UnauthorizedException e){
+        PrintWriter writer = null;
+        try{
+            //判断是否是ajax
+            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            HttpServletRequest request = requestAttributes.getRequest();
+            HttpServletResponse response = requestAttributes.getResponse();
+            String header = request.getHeader("X-Requested-With");
+            if(StringUtils.isNoneBlank(header) && "XMLHttpRequest".equalsIgnoreCase(header)){
+                response.setCharacterEncoding("UTF-8");
+                response.setContentType("application/json; charset=utf-8");
+                writer = response.getWriter();
+//                {"status":401,"message":"无权访问"}
+//                String respStr = ""
+                writer.write("{\"status\":401,\"message\":\"无权访问\"}");
+            }else{
+                String contextPath = request.getContextPath();
+                if("/".equals(contextPath))
+                    contextPath = "";
+                response.sendRedirect(request.getContextPath() + "/page/toDenied");
+            }
+        }catch (IOException io){
+            io.printStackTrace();
+        }finally {
+            if(writer != null)
+                writer.close();
+        }
+    }
+
+    @ExceptionHandler(UnauthenticatedException.class)
+    public void calUnauthorizedException(UnauthenticatedException e){
+        PrintWriter writer = null;
+        try{
+            //判断是否是异步请求
+            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            HttpServletRequest request = requestAttributes.getRequest();
+            HttpServletResponse response = requestAttributes.getResponse();
+            String header = request.getHeader("X-Requested-With");
+            if(StringUtils.isNoneBlank(header) && "XMLHttpRequest".equalsIgnoreCase(header)){
+                response.setCharacterEncoding("UTF-8");
+                response.setContentType("application/json; charset=utf-8");
+                writer = response.getWriter();
+//                {"status":401,"message":"无权访问"}
+//                String respStr = ""
+                writer.write("{\"status\":302,\"message\":\"请前去登录\"}");
+            }else{
+                String contextPath = request.getContextPath();
+                if("/".equals(contextPath))
+                    contextPath = "";
+                response.sendRedirect(request.getContextPath() + "/backend/toLogin");
+            }
+        }catch (IOException io){
+            io.printStackTrace();
+        }finally {
+            if(writer != null)
+                writer.close();
+        }
+    }
+
+}

+ 26 - 0
src/main/java/com/itheima/mapper/AdministratorMapper.java

@@ -0,0 +1,26 @@
+package com.itheima.mapper;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.itheima.pojo.Administrator;
+import org.apache.ibatis.annotations.Select;
+import org.springframework.stereotype.Repository;
+
+import java.util.Set;
+
+/**
+ *
+ */
+@Repository
+public interface AdministratorMapper extends BaseMapper<Administrator> {
+
+    @Select("select distinct r.code" +
+            " from tb_role r,tb_admin u,tb_admin_role ur" +
+            " where r.id = ur.role_id and u.id = ur.admin_id and u.username = #{username}")
+    Set<String> findRolesByUsername(String username);
+
+    @Select("select distinct p.code " +
+            "from tb_role r,tb_admin u,tb_admin_role ur,tb_role_perm rp,tb_permission p " +
+            "where r.id = ur.role_id and u.id = ur.admin_id and rp.role_id = r.id and rp.perm_id = p.id " +
+            "and u.username = #{username}")
+    Set<String> findPermissionsByUsername(String username);
+}

+ 10 - 0
src/main/java/com/itheima/mapper/StorageMapper.java

@@ -0,0 +1,10 @@
+package com.itheima.mapper;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.itheima.pojo.Storage;
+
+/**
+ *
+ */
+public interface StorageMapper extends BaseMapper<Storage> {
+}

+ 39 - 0
src/main/java/com/itheima/pojo/Administrator.java

@@ -0,0 +1,39 @@
+package com.itheima.pojo;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.enums.IdType;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author WBH
+ * @date 2020/4/25 21:52
+ */
+@Data
+@TableName("tb_admin")
+public class Administrator implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    private String username;
+
+    private String password;
+
+    private String realname;
+
+    private String gender;
+
+    private String privateSalt; //私有盐,用户密码加密
+
+    private String tel;
+
+    private String userStatus;
+
+    @TableField(exist = false)
+    private List<Role> roleList;
+}

+ 16 - 0
src/main/java/com/itheima/pojo/Permission.java

@@ -0,0 +1,16 @@
+package com.itheima.pojo;
+
+import com.baomidou.mybatisplus.annotations.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@TableName("tb_permission")
+public class Permission implements Serializable {
+    private Integer id;
+    private String name;
+    private String code;
+    private String url;
+//    private String permType;//0:资源菜单 1资源下的权限
+}

+ 19 - 0
src/main/java/com/itheima/pojo/Role.java

@@ -0,0 +1,19 @@
+package com.itheima.pojo;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableName;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@TableName("tb_role")
+public class Role {
+    private Integer id;
+    private String name;
+    private String code;
+    private String intro;
+
+    @TableField(exist = false)
+    private List<Permission> permissionList;
+}

+ 16 - 0
src/main/java/com/itheima/pojo/Storage.java

@@ -0,0 +1,16 @@
+package com.itheima.pojo;
+
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.enums.IdType;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@TableName("tb_storage")
+public class Storage implements Serializable {
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+    private String name;
+}

+ 18 - 0
src/main/java/com/itheima/service/AdminService.java

@@ -0,0 +1,18 @@
+package com.itheima.service;
+
+import com.itheima.pojo.Administrator;
+
+import java.util.List;
+import java.util.Set;
+
+public interface AdminService {
+    List<Administrator> findAll();
+
+    void deleteById(Integer id);
+
+    Administrator findUserByUsername(String username);
+
+    Set<String> findRolesByUsername(String username);
+
+    Set<String> findPermissionsByUsername(String username);
+}

+ 13 - 0
src/main/java/com/itheima/service/StorageService.java

@@ -0,0 +1,13 @@
+package com.itheima.service;
+
+import com.itheima.pojo.Storage;
+
+import java.util.List;
+
+/**
+ *
+ */
+public interface StorageService {
+
+    List<Storage> findAll();
+}

+ 70 - 0
src/main/java/com/itheima/service/impl/AdminServiceImpl.java

@@ -0,0 +1,70 @@
+package com.itheima.service.impl;
+
+
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.baomidou.mybatisplus.mapper.Wrapper;
+import com.itheima.mapper.AdministratorMapper;
+import com.itheima.pojo.Administrator;
+import com.itheima.service.AdminService;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.shiro.crypto.hash.Md5Hash;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
+ */
+@Service
+public class AdminServiceImpl implements AdminService {
+
+    @Autowired
+    private AdministratorMapper administratorMapper;
+
+    @Override
+    public List<Administrator> findAll() {
+        return administratorMapper.selectList(null);
+    }
+
+    @Override
+    public void deleteById(Integer id) {
+        administratorMapper.deleteById(id);
+    }
+
+    @Override
+    public Administrator findUserByUsername(String username) {
+        Wrapper<Administrator> wrapper = new EntityWrapper<Administrator>();
+        wrapper.eq("username",username);
+        List<Administrator> adminList = administratorMapper.selectList(wrapper);
+        if(adminList != null && adminList.size() > 0){
+            return adminList.get(0);
+        }
+        return null;
+    }
+
+    @Override
+    public Set<String> findRolesByUsername(String username) {
+        return administratorMapper.findRolesByUsername(username);
+    }
+
+    @Override
+    public Set<String> findPermissionsByUsername(String username) {
+        return administratorMapper.findPermissionsByUsername(username);
+    }
+
+    public void saveAdmin(Administrator admin) {
+        String password = admin.getPassword();
+        String salt = RandomStringUtils.randomNumeric(6,8); //使用
+        admin.setPrivateSalt(salt);
+
+        Md5Hash md5Hash = new Md5Hash(password,salt); //模拟md5加密一次
+        admin.setPassword(md5Hash.toString());
+
+        admin.setUserStatus("1");
+
+        administratorMapper.insert(admin);
+    }
+}

+ 23 - 0
src/main/java/com/itheima/service/impl/StorageServiceImpl.java

@@ -0,0 +1,23 @@
+package com.itheima.service.impl;
+
+
+import com.itheima.mapper.StorageMapper;
+import com.itheima.pojo.Storage;
+import com.itheima.service.StorageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@Transactional
+public class StorageServiceImpl implements StorageService {
+
+    @Autowired
+    private StorageMapper storageMapper;
+
+    public List<Storage> findAll(){
+        return storageMapper.selectList(null);
+    }
+}

+ 60 - 0
src/main/java/com/itheima/shiroConfig/MyRealm.java

@@ -0,0 +1,60 @@
+package com.itheima.shiroConfig;
+
+import com.itheima.pojo.Administrator;
+import com.itheima.service.AdminService;
+import org.apache.shiro.authc.*;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.util.ByteSource;
+import org.apache.shiro.util.SimpleByteSource;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Set;
+
+/**
+ * 和数据库交互 看看用户信息 权限信息
+ */
+public class MyRealm extends AuthorizingRealm {
+
+    @Autowired
+    private AdminService adminService;
+
+    //获取权限信息的方法
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        System.out.println("走了权限查询方法");
+        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
+        //获取用户名
+        Administrator administrator = (Administrator)principals.getPrimaryPrincipal();//此Principal就是彼Principal(认证时构造的)
+        String username = administrator.getUsername();
+        //从数据库查询用户的权限和角色
+        Set<String> roles = adminService.findRolesByUsername(username);
+        if(roles != null && roles.size() > 0){
+            simpleAuthorizationInfo.addRoles(roles);
+        }
+        Set<String> permissions = adminService.findPermissionsByUsername(username);
+        if(permissions != null && permissions.size() > 0){
+            simpleAuthorizationInfo.addStringPermissions(permissions);
+        }
+        return simpleAuthorizationInfo;
+    }
+
+    //获取认证信息的方法
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+        //token是封装好的用户提交的用户名密码
+        String username = ((UsernamePasswordToken) token).getUsername();
+        //获取用户
+        Administrator administrator = adminService.findUserByUsername(username);
+        if(administrator == null){
+            return null;
+        }else{
+            //封装AuthenticationInfo
+            ByteSource bsSalt = new SimpleByteSource(administrator.getPrivateSalt());
+            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(administrator,administrator.getPassword(),bsSalt,getName());
+            return authenticationInfo;
+        }
+    }
+}

+ 25 - 0
src/main/java/com/itheima/shiroConfig/MyRedisCacheManager.java

@@ -0,0 +1,25 @@
+package com.itheima.shiroConfig;
+
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheException;
+import org.apache.shiro.cache.CacheManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.stream.Stream;
+
+/**
+ *
+ */
+public class MyRedisCacheManager implements CacheManager {
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Override
+    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
+        return new ShiroRedisCache(name,redisTemplate);
+    }
+
+}

+ 141 - 0
src/main/java/com/itheima/shiroConfig/ShiroConfig.java

@@ -0,0 +1,141 @@
+package com.itheima.shiroConfig;
+
+import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
+import org.apache.shiro.authc.credential.CredentialsMatcher;
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.cache.CacheManager;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.session.mgt.eis.SessionDAO;
+import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+@Configuration
+public class ShiroConfig {
+    //shiroFilter
+    @Bean
+    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
+        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+        shiroFilterFactoryBean.setSecurityManager(securityManager);
+        shiroFilterFactoryBean.setLoginUrl("/page/toLogin");
+
+        //控制 访问xx资源 需要xx权限
+        Map filterChainMap = new LinkedHashMap<String,String>();
+        filterChainMap.put("/page/toLogin","anon"); //访问登录页面 直接放行
+        filterChainMap.put("/","anon"); //访问登录页面 直接放行
+        filterChainMap.put("/user/all","perms[user:select]"); //查询所有用户 需要认证(登录)
+
+        //当用户查看仓库列表时,需要有仓库权限
+        filterChainMap.put("/storage/all","perms[storage:select]");
+        //当用户删除用户时,需要有超级管理员角色
+//        filterChainMap.put("/user/del/*","roles[role_superman]");
+
+        filterChainMap.put("/backend/logout","logout");
+
+        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
+        return shiroFilterFactoryBean;
+    }
+
+    //安全管理器
+    @Bean
+    public SecurityManager securityManager() {
+        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+        securityManager.setSessionManager(sessionManager());
+        securityManager.setRealm(myRealm());
+        return securityManager;
+    }
+
+    //realm
+    @Bean
+    public Realm myRealm(){
+        MyRealm myRealm = new MyRealm();
+        //告诉realm密码匹配方式
+        myRealm.setCredentialsMatcher(credentialsMatcher());
+        myRealm.setAuthorizationCacheName("perms");
+        myRealm.setAuthorizationCachingEnabled(true);
+
+        myRealm.setAuthenticationCachingEnabled(false);
+        //设置缓存管理器
+        myRealm.setCacheManager(cacheManager());
+        return myRealm;
+    }
+
+    //缓存管理
+    @Bean
+    public CacheManager cacheManager(){
+        MyRedisCacheManager cacheManager = new MyRedisCacheManager();
+        return cacheManager;
+    }
+
+    @Bean
+    public CredentialsMatcher credentialsMatcher(){
+        HashedCredentialsMatcher hashedMatcher = new HashedCredentialsMatcher();
+        hashedMatcher.setHashAlgorithmName("md5");
+//        hashedMatcher.setHashIterations(1);
+        return hashedMatcher;
+    }
+
+    /**
+     * 注解支持
+     */
+    @Bean
+    @ConditionalOnMissingBean
+    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
+        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
+        defaultAAP.setProxyTargetClass(true);
+        return defaultAAP;
+    }
+
+    @Bean
+    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
+        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
+        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
+        return authorizationAttributeSourceAdvisor;
+    }
+
+    @Bean
+    public ShiroDialect shiroDialect(){
+        return new ShiroDialect();
+    }
+
+    /**
+     * 会话管理器
+     * @return
+     */
+    @Bean
+    public SessionManager sessionManager() {
+        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
+        sessionManager.setSessionDAO(redisSessionDAO());
+
+        //设置会话过期时间
+        sessionManager.setGlobalSessionTimeout(3*60*1000); //默认半小时
+        sessionManager.setDeleteInvalidSessions(true); //默认自定调用SessionDAO的delete方法删除会话
+        //设置会话定时检查
+        //        sessionManager.setSessionValidationInterval(180000); //默认一小时
+        //        sessionManager.setSessionValidationSchedulerEnabled(true);
+        return sessionManager;
+    }
+
+    @Bean
+    public SessionDAO redisSessionDAO(){
+        ShiroRedisSessionDao redisDAO = new ShiroRedisSessionDao();
+        return redisDAO;
+    }
+
+
+
+}

+ 227 - 0
src/main/java/com/itheima/shiroConfig/ShiroRedisCache.java

@@ -0,0 +1,227 @@
+package com.itheima.shiroConfig;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ *
+ */
+public class ShiroRedisCache<K, V> implements Cache<K, V> {
+    private static Logger LOGGER = LogManager.getLogger(ShiroRedisCache.class);
+
+    /**
+     * key前缀
+     */
+    private static final String REDIS_SHIRO_CACHE_KEY_PREFIX = "shiro_cache_key_";
+
+    /**
+     * cache name
+     */
+    private String name;
+
+    /**
+     * jedis 连接工厂
+     */
+
+    private RedisTemplate redisTemplate;
+
+    /**
+     * 序列化工具
+     */
+    private RedisSerializer serializer = new JdkSerializationRedisSerializer();
+
+    /**
+     * 存储key的redis.list的key值
+     */
+    private String keyListKey;
+
+    private RedisConnection getConnection(){
+        return this.redisTemplate.getConnectionFactory().getConnection();
+    }
+
+    public ShiroRedisCache(String name,RedisTemplate redisTemplate) {
+        this.name = name;
+        this.redisTemplate = redisTemplate;
+        this.keyListKey = REDIS_SHIRO_CACHE_KEY_PREFIX + name;
+    }
+
+    @Override
+    public V get(K key) throws CacheException {
+        LOGGER.debug("shiro redis cache get.{} K={}", name, key);
+        RedisConnection redisConnection = null;
+        V result = null;
+        try {
+            redisConnection = getConnection();
+            result = (V) serializer.deserialize(redisConnection.get(serializer.serialize(generateKey(key))));
+        } catch (Exception e) {
+            LOGGER.error("shiro redis cache get exception. ", e);
+        } finally {
+            if (null != redisConnection) {
+                redisConnection.close();
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public V put(K key, V value) throws CacheException {
+        LOGGER.debug("shiro redis cache put.{} K={} V={}", name, key, value);
+        RedisConnection redisConnection = null;
+        V result = null;
+        try {
+            redisConnection = getConnection();
+            result = (V) serializer.deserialize(redisConnection.get(serializer.serialize(generateKey(key))));
+
+            redisConnection.set(serializer.serialize(generateKey(key)), serializer.serialize(value));
+
+            redisConnection.lPush(serializer.serialize(keyListKey), serializer.serialize(generateKey(key)));
+        } catch (Exception e) {
+            LOGGER.error("shiro redis cache put exception. ", e);
+        } finally {
+            if (null != redisConnection) {
+                redisConnection.close();
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public V remove(K key) throws CacheException {
+        LOGGER.debug("shiro redis cache remove.{} K={}", name, key);
+        RedisConnection redisConnection = null;
+        V result = null;
+        try {
+            redisConnection = getConnection();
+            result = (V) serializer.deserialize(redisConnection.get(serializer.serialize(generateKey(key))));
+
+            redisConnection.expireAt(serializer.serialize(generateKey(key)), 0);
+
+            redisConnection.lRem(serializer.serialize(keyListKey), 1, serializer.serialize(key));
+        } catch (Exception e) {
+            LOGGER.error("shiro redis cache remove exception. ", e);
+        } finally {
+            if (null != redisConnection) {
+                redisConnection.close();
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public void clear() throws CacheException {
+        LOGGER.debug("shiro redis cache clear.{}", name);
+        RedisConnection redisConnection = null;
+        try {
+            redisConnection = getConnection();
+
+            Long length = redisConnection.lLen(serializer.serialize(keyListKey));
+            if (0 == length) {
+                return;
+            }
+
+            List<byte[]> keyList = redisConnection.lRange(serializer.serialize(keyListKey), 0, length - 1);
+            for (byte[] key : keyList) {
+                redisConnection.expireAt(key, 0);
+            }
+
+            redisConnection.expireAt(serializer.serialize(keyListKey), 0);
+            keyList.clear();
+        } catch (Exception e) {
+            LOGGER.error("shiro redis cache clear exception.", e);
+        } finally {
+            if (null != redisConnection) {
+                redisConnection.close();
+            }
+        }
+    }
+
+    @Override
+    public int size() {
+        LOGGER.debug("shiro redis cache size.{}", name);
+        RedisConnection redisConnection = null;
+        int length = 0;
+        try {
+            redisConnection = getConnection();
+            length = Math.toIntExact(redisConnection.lLen(serializer.serialize(keyListKey)));
+        } catch (Exception e) {
+            LOGGER.error("shiro redis cache size exception.", e);
+        } finally {
+            if (null != redisConnection) {
+                redisConnection.close();
+            }
+        }
+        return length;
+    }
+
+    @Override
+    public Set keys() {
+        LOGGER.debug("shiro redis cache keys.{}", name);
+        RedisConnection redisConnection = null;
+        Set resultSet = null;
+        try {
+            redisConnection = getConnection();
+
+            Long length = redisConnection.lLen(serializer.serialize(keyListKey));
+            if (0 == length) {
+                return resultSet;
+            }
+
+            List<byte[]> keyList = redisConnection.lRange(serializer.serialize(keyListKey), 0, length - 1);
+            resultSet = keyList.stream().map(bytes -> serializer.deserialize(bytes)).collect(Collectors.toSet());
+        } catch (Exception e) {
+            LOGGER.error("shiro redis cache keys exception.", e);
+        } finally {
+            if (null != redisConnection) {
+                redisConnection.close();
+            }
+        }
+        return resultSet;
+    }
+
+    @Override
+    public Collection values() {
+        RedisConnection redisConnection = getConnection();
+        Set keys = this.keys();
+
+        List<Object> values = new ArrayList<Object>();
+        for (Object key : keys) {
+            byte[] bytes = redisConnection.get(serializer.serialize(key));
+            values.add(serializer.deserialize(bytes));
+        }
+        return values;
+    }
+
+    /**
+     * 重组key
+     * 区别其他使用环境的key
+     *
+     * @param key
+     * @return
+     */
+    private String generateKey(K key) {
+        return REDIS_SHIRO_CACHE_KEY_PREFIX + name + "_" + key;
+    }
+
+    private byte[] getByteKey(K key) {
+        if (key instanceof String) {
+            String preKey = generateKey(key);
+            return preKey.getBytes();
+        }
+        return serializer.serialize(key);
+    }
+}

+ 69 - 0
src/main/java/com/itheima/shiroConfig/ShiroRedisSessionDao.java

@@ -0,0 +1,69 @@
+package com.itheima.shiroConfig;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.io.Serializable;
+
+/**
+ *
+ */
+public class ShiroRedisSessionDao extends CachingSessionDAO {
+
+    public static final String SHIRO_SESSION_KEY = "shiro_session_key";
+
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private RedisTemplate redisTemplate; //spring-data-redis
+
+    @Override
+    protected void doUpdate(Session session) {
+        this.saveSession(session);
+    }
+
+    @Override
+    protected void doDelete(Session session) {
+        if (session == null || session.getId() == null) {
+            logger.error("session or session id is null");
+            return ;
+        }
+        //根据session id删除session
+        redisTemplate.boundHashOps(SHIRO_SESSION_KEY).delete(session.getId());
+    }
+
+
+    @Override
+    protected Serializable doCreate(Session session) {
+        Serializable sessionId = this.generateSessionId(session);
+        this.assignSessionId(session, sessionId);
+        this.saveSession(session);
+        return sessionId;
+    }
+
+
+    @Override
+    protected Session doReadSession(Serializable sessionId) {
+        if(sessionId == null){
+            logger.error("传入的 session id is null");
+            return null;
+        }
+        return (Session)redisTemplate.boundHashOps(SHIRO_SESSION_KEY).get(sessionId);
+    }
+
+    /**
+     * 将session 保存进redis 中
+     * @param session 要保存的session
+     */
+    private void saveSession(Session session) {
+        if (session == null || session.getId() == null) {
+            logger.error("session or session id is null");
+            return ;
+        }
+        redisTemplate.boundHashOps(SHIRO_SESSION_KEY).put(session.getId(),session);
+    }
+}

+ 12 - 0
src/main/resources/application.yml

@@ -0,0 +1,12 @@
+spring:
+  datasource:
+    url: jdbc:mysql://127.0.0.1/shiro?characterEncoding=utf8&serverTimezone=Asia/Shanghai
+    username: root
+    password: root
+    driver-class-name: com.mysql.cj.jdbc.Driver
+mybatis-plus:
+  configuration:
+    map-underscore-to-camel-case: true #驼峰命名转换
+logging:
+  com.itheima:
+    level: debug

File diff suppressed because it is too large
+ 1 - 0
src/main/resources/static/js/jquery.js


+ 10 - 0
src/main/resources/templates/denied.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>401错误</title>
+</head>
+<body>
+ 无权访问
+</body>
+</html>

+ 14 - 0
src/main/resources/templates/index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="https://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <meta charset="UTF-8">
+    <title>欢迎光临</title>
+</head>
+<body>
+    网站首页,
+    欢迎光临<shiro:principal property="username"></shiro:principal>
+
+    <a shiro:hasRole="role_superman" href="/user/del/1">删除用户ID=1</a>
+    <a shiro:hasAnyPermissions="user:select,user:del:id">查看所有</a>
+</body>
+</html>

+ 15 - 0
src/main/resources/templates/login.html

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="https://www.thymeleaf.org/">
+<head>
+    <meta charset="UTF-8">
+    <title>登录页面</title>
+</head>
+<body>
+    <h5 th:text="${errMessage}"></h5>
+    <form action="/backend/login" method="post">
+        <input name="username"/><br>
+        <input name="password"/><br>
+        <input type="submit" value="登录"/>
+    </form>
+</body>
+</html>

+ 16 - 0
src/main/resources/templates/success.html

@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+</head>
+<body>
+    登录访问成功,3S后跳转到首页
+    <script>
+        setTimeout(fun,3000)
+        function fun() {
+            location.href="/"
+        }
+    </script>
+</body>
+</html>

+ 32 - 0
src/test/java/com/itheima/test/DAOTest.java

@@ -0,0 +1,32 @@
+package com.itheima.test;
+
+import com.itheima.mapper.StorageMapper;
+import com.itheima.pojo.Storage;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+/**
+ *
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class DAOTest {
+
+    @Autowired
+    private StorageMapper storageMapper;
+
+    @Test
+    public void findAllSRO(){
+        List<Storage> storages = storageMapper.selectList(null);
+        for (Storage storage : storages) {
+            System.out.println(storage);
+        }
+    }
+
+
+}

Some files were not shown because too many files changed in this diff