kotlin+gradle+springboot+shiro

依赖版本:
在这里插入图片描述
在这里插入图片描述
图上有nacos的依赖nacos的服务注册和服务发现可以不需要,链接数据库连接池必须有, // thymeleaf依赖是为了展示页面
implementation(“org.springframework.boot:spring-boot-starter-web”)
// shiro
implementation(“org.apache.shiro:shiro-web:1.8.0”)
implementation(“org.apache.shiro:shiro-spring:1.8.0”)
implementation(“org.apache.shiro:shiro-core:1.8.0”)

//数据库
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4")
//HikariCP连接池
implementation("com.zaxxer:HikariCP:3.3.1")
implementation("mysql:mysql-connector-java:8.0.11")
// thymeleaf-extras-shiro
implementation("com.github.theborakompanioni:thymeleaf-extras-shiro:2.0.0")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf:2.5.4")

项目目录
在这里插入图片描述
下面的/s/addUserView的这些路径看自己项目写(页面不能成功拦截先去看自己的路径对不对)
/**

  • Shiro的配置类

  • @author lenovo
    /
    @Configuration
    class ShiroConfig {
    /
    *

    • 创建ShiroFilterFactoryBean
      */
      @Bean
      fun getShiroFilterFactoryBean(@Qualifier(“securityManager”) securityManager: DefaultWebSecurityManager?): ShiroFilterFactoryBean {
      val shiroFilterFactoryBean = ShiroFilterFactoryBean()

      //设置安全管理器
      shiroFilterFactoryBean.securityManager = securityManager
      //添加Shiro内置过滤器
      /**

      • Shiro内置过滤器,可以实现权限相关的拦截器
      • 常用的过滤器:
      • anon: 无需认证(登录)可以访问
      • authc: 必须认证才可以访问
      • user: 如果使用rememberMe的功能可以直接访问
      • perms: 该资源必须得到资源权限才可以访问
      • role: 该资源必须得到角色权限才可以访问
        /
        val filterMap: MutableMap<String, String> = LinkedHashMap()
        // filterMap.put("/s/add", “authc”);
        // filterMap.put("/s/update", “authc”);
        //放行login.html页面
        filterMap["/s/login"] = “anon”
        filterMap["/s/addUserView"] = “anon”
        filterMap["/s/addUser"] = “anon”
        filterMap["/s/
        "] = “authc”

      //授权过滤器
      //注意:当前授权拦截后,shiro会自动跳转到未授权页面
      filterMap["/s/add"] = “perms[user:add]”
      filterMap["/s/update"] = “perms[user:update]”
      // filterMap["/findAll"] = “perms[user:findAll]”
      //filterMap["/s/*"] = “authc”

      //修改调整的登录页面
      shiroFilterFactoryBean.loginUrl = “/s/toLogin”
      //设置未授权提示页面
      shiroFilterFactoryBean.unauthorizedUrl = “/s/noAuth”
      shiroFilterFactoryBean.filterChainDefinitionMap = filterMap
      return shiroFilterFactoryBean
      }

    /**

    • 创建DefaultWebSecurityManager
      */
      @Bean(name = [“securityManager”])
      fun getDefaultWebSecurityManager(@Qualifier(“userRealm”) userRealm: UserRealm?): DefaultWebSecurityManager {
      val securityManager = DefaultWebSecurityManager()
      //关联realm
      securityManager.setRealm(userRealm)
      return securityManager
      }

    /**

    • 创建Realm
      */
      @Bean(name = [“userRealm”])
      fun getRealm(): UserRealm? {
      return UserRealm()
      }

    /**

    • 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
      */
      @Bean
      fun getShiroDialect(): ShiroDialect? {
      return ShiroDialect()
      }

    /**

    • 凭证匹配器
    • (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
    • @return
      */

    @Bean
    fun hashedCredentialsMatcher(): HashedCredentialsMatcher? {
    val hashedCredentialsMatcher = HashedCredentialsMatcher()
    hashedCredentialsMatcher.hashAlgorithmName = “md5” //散列算法:这里使用MD5算法;
    hashedCredentialsMatcher.hashIterations = 1024 //散列的次数,比如散列两次,相当于 md5(md5(""));
    return hashedCredentialsMatcher
    }

    //将自己的验证方式加入容器
    @Bean
    fun myShiroRealm(): UserRealm? {
    val myShiroRealm = UserRealm()
    myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher())
    return myShiroRealm
    }

}

Usercontroller:

@Controller
@RequestMapping("/user")
class UserController {

@Resource
lateinit var userService: UserService

/**
 * 测试方法
 */
@RequestMapping("/addUserView")
fun hello(): String {
    return "/addUser"
}

@RequestMapping("/add")
fun add(): String {
    return "/user/add"
}

@RequestMapping("/update")
fun update(): String {
    return "/user/update"
}

@RequestMapping("/toLogin")
fun toLogin(): String {
    return "/login"
}

//通过controller返回html界面
@RequestMapping("/index")
fun indexJumpPage(): String {
    return "/login"
}

// 未授权
@RequestMapping("/noAuth")
fun noAuth(): String {
    return "/noAuth"
}



/**
 * 全查
 */
@RequestMapping("/findAll")
@ResponseBody
fun findAll(id: String): List<User>? {
    //把数据存入model
    // print(userService.findAll(id).toString())
    //返回test.html
    val u: List<User> = userService.findAll(id)!!
    return u
}

/**
 * 登录逻辑处理
 */
@PostMapping("/login")
fun login(name: String, password: String, model: Model): String {

    /**
     * 使用Shiro编写认证操作
     */
    //1.获取Subject
    val subject = SecurityUtils.getSubject()
    val u = userService.findByName(name)
    // 两个参数,第一个是需要加密的字符串,第二个是盐
    var hash = Md5Hash(password, name.toString() + u?.salt)
    var hashpassword = hash.toString()
    //2.封装用户数据
    val token = UsernamePasswordToken(name, hashpassword)

    //3.执行登录方法
    return try {

        // 没有异常登录成功
        subject.login(token)
        //登录成功
        return "test"
    } catch (e: UnknownAccountException) {
        //e.printStackTrace();
        //登录失败:用户名不存在
        model.addAttribute("msg", "用户名不存在")
        return "login"
    } catch (e: IncorrectCredentialsException) {
        //e.printStackTrace();
        //登录失败:密码错误
         print("密码错误")
        model.addAttribute("msg", "密码错误")
        return "login"
    }
}

/**
 * 注册加密(md5+salt(盐))
 */
@PostMapping("/addUser")
fun addUser(user: User):String {
    // 生成一个随机4位数字符串(只包含大小写字母,数字)
    val salt: String = RandomUtil.generateString(4)
    user.salt= salt
    user.id = UUID.randomUUID().toString()
    user.perms = "user:add"
    // 两个参数,第一个是需要加密的字符串,第二个是盐
    val hash = Md5Hash(user.password, user.name.toString() + salt)
    user.password =hash.toString()
    userService.addUser(user)
    return "login"
}

}
实体类
在这里插入图片描述
@Mapper
interface UserMapper{
@Select(
“SELECT id,NAME,PASSWORD,perms,salt FROM user where name = #{value}”
)
fun findByName(name: String?): User?

@Select("SELECT id,NAME,PASSWORD,perms FROM user where id = #{value}")
fun findById(id: String?): User?

@Select("select*from user where id = #{id}")
fun findAll(id:String?) :List<User>?

@Insert("insert into user(id,name,password,perms,salt) values(#{id},#{name},#{password},#{perms},#{salt})")
fun addUser(user : User)

}

RandomUtil类加密方式

class RandomUtil : Random {
constructor() : super() {}
constructor(seed: Long) : super(seed) {}

fun nextInt(n: Int, size: Int): IntArray {
    var size = size
    if (size > n) {
        size = n
    }
    val set: MutableSet<*> = LinkedHashSet<Any?>()
    for (i in 0 until size) {
        while (true) {
            val value: Int = nextInt(n)
            if (!set.contains(value)) {
                set.add(value as Nothing)
                break
            }
        }
    }
    val array = IntArray(set.size)
    val itr: Iterator<*> = set.iterator()
    for (i in array.indices) {
        array[i] = (itr.next() as Int).toInt()
    }
    return array
}

fun randomize(array: CharArray) {
    var length = array.size
    for (i in 0 until length - 1) {
        val x: Int = nextInt(length)
        val y = array[i]
        array[i] = array[i + x]
        array[i + x] = y
        length--
    }
}

fun randomize(array: IntArray) {
    var length = array.size
    for (i in 0 until length - 1) {
        val x: Int = nextInt(length)
        val y = array[i]
        array[i] = array[i + x]
        array[i + x] = y
        length--
    }
}

fun randomize(list: MutableList<*>) {
    var size = list.size
    for (i in 0..size) {
        val j: Int = nextInt(size)
        val obj = list[i]!!
        list.set(i, list[i + j] as Nothing)
        list.set(i + j, obj as Nothing)
        size--
    }
}

fun randomize(array: Array<Any?>) {
    var length = array.size
    for (i in 0 until length - 1) {
        val x: Int = nextInt(length)
        val y = array[i]
        array[i] = array[i + x]
        array[i + x] = y
        length--
    }
}

fun randomize(s: String?): String? {
    if (s == null) {
        return null
    }
    val array = s.toCharArray()
    randomize(array)
    return String(array)
}

companion object {
    /**
     * 所有大小写字母和数字
     */
    val allChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

    /**
     * 所有大小写字母
     */
    val letterChar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

    /**
     * 所有数字
     */
    val numberChar = "0123456789"

    /**
     * 返回一个定长的随机纯数字字符串(只包含数字)
     * @param length
     * 随机字符串长度
     * @return 随机字符串
     */
    fun generateDigitalString(length: Int): String {
        val sb = StringBuffer()
        val random = Random()
        for (i in 0 until length) {
            sb.append(numberChar[random.nextInt(numberChar.length)])
        }
        return sb.toString()
    }

    /**
     * 返回一个定长的随机字符串(只包含大小写字母、数字)
     * @param length
     * 随机字符串长度
     * @return 随机字符串
     */
    fun generateString(length: Int): String {
        val sb = StringBuffer()
        val random = Random()
        for (i in 0 until length) {
            sb.append(allChar[random.nextInt(allChar.length)])
        }
        return sb.toString()
    }

    /**
     * 返回一个定长的随机纯字母字符串(只包含大小写字母)
     * @param length
     * 随机字符串长度
     * @return 随机字符串
     */
    fun generateMixString(length: Int): String {
        val sb = StringBuffer()
        val random = Random()
        for (i in 0 until length) {
            sb.append(letterChar[random.nextInt(letterChar.length)])
        }
        return sb.toString()
    }

    /**
     * 返回一个定长的随机纯大写字母字符串(只包含大小写字母)
     * @param length
     * 随机字符串长度
     * @return 随机字符串
     */
    fun generateLowerString(length: Int): String {
        return generateMixString(length).toLowerCase()
    }

    /**
     * 返回一个定长的随机纯小写字母字符串(只包含大小写字母)
     * @param length
     * 随机字符串长度
     * @return 随机字符串
     */
    fun generateUpperString(length: Int): String {
        return generateMixString(length).toUpperCase()
    }

    /**
     * 生成一个定长的纯0字符串
     * @param length
     * 字符串长度
     * @return 纯0字符串
     */
    fun generateZeroString(length: Int): String {
        val sb = StringBuffer()
        for (i in 0 until length) {
            sb.append('0')
        }
        return sb.toString()
    }

    /**
     * 根据数字生成一个定长的字符串,长度不够前面补0
     * @param num
     * 数字
     * @param fixdlenth
     * 字符串长度
     * @return 定长的字符串
     */
    fun toFixdLengthString(num: Long, fixdlenth: Int): String {
        val sb = StringBuffer()
        val strNum = num.toString()
        if (fixdlenth - strNum.length >= 0) {
            sb.append(generateZeroString(fixdlenth - strNum.length))
        } else {
            throw RuntimeException(
                "将数字" + num + "转化为长度为" + fixdlenth
                        + "的字符串发生异常!"
            )
        }
        sb.append(strNum)
        return sb.toString()
    }

    /**
     * 根据数字生成一个定长的字符串,长度不够前面补0
     * @param num
     * 数字
     * @param fixdlenth
     * 字符串长度
     * @return 定长的字符串
     */
    fun toFixdLengthString(num: Int, fixdlenth: Int): String {
        val sb = StringBuffer()
        val strNum = num.toString()
        if (fixdlenth - strNum.length >= 0) {
            sb.append(generateZeroString(fixdlenth - strNum.length))
        } else {
            throw RuntimeException(
                ("将数字" + num + "转化为长度为" + fixdlenth
                        + "的字符串发生异常!")
            )
        }
        sb.append(strNum)
        return sb.toString()
    }

    /**
     * serialVersionUID:TODO(用一句话描述这个变量表示什么)
     *
     * @since 1.0.0
     */
    private val serialVersionUID = 1L
    val instance = RandomUtil()
}

}
UserRealm:

/**

  • 自定义Realm

  • @author lenovo
    /
    class UserRealm : AuthorizingRealm() {
    /
    *

    • 执行授权逻辑
      */
      override fun doGetAuthorizationInfo(arg0: PrincipalCollection): AuthorizationInfo {
      // println(“执行授权逻辑”)

      //给资源进行授权
      val info = SimpleAuthorizationInfo()

      //添加资源的授权字符串
      // info.addStringPermission(“user:findAll”);

      //到数据库查询当前登录用户的授权字符串
      //获取当前登录用户
      val subject = SecurityUtils.getSubject()
      val user = subject.principal as User
      val dbUser = userSerivce!!.findById(user.id)
      info.addStringPermission(dbUser!!.perms)
      return info
      }

    @Autowired
    private val userSerivce: UserService? = null

    /**

    • 执行认证逻辑
      */
      @Throws(AuthenticationException::class)
      override fun doGetAuthenticationInfo(arg0: AuthenticationToken): AuthenticationInfo? {
      // println(“执行认证逻辑”)

      //编写shiro判断逻辑,判断用户名和密码
      //1.判断用户名
      val token = arg0 as UsernamePasswordToken
      val user = userSerivce!!.findByName(token.username) ?: //用户名不存在

      return null //shiro底层会抛出UnKnowAccountException
      //当前realm对象的name
      val realmName = name
      //盐值
      val credentialsSalt: ByteSource = ByteSource.Util.bytes(user.name.toString() + user.salt)
      //2.判断密码
      //封装用户信息,构建AuthenticationInfo对象并返回
      return SimpleAuthenticationInfo(
      user, user.password,
      credentialsSalt, realmName
      )

    }
    }
    在这里插入图片描述
    @Service
    class UserServiceImpl : UserService {
    //注入Mapper接口
    @Autowired
    private val userMapper: UserMapper? = null
    override fun findByName(name: String?): User? {
    return userMapper!!.findByName(name)
    }

    override fun findById(id: String?): User? {
    return userMapper!!.findById(id)
    }

    override fun findAll(id: String?): List? {
    return userMapper!!.findAll(id)
    }

    /**

    • 注册(加密)
      */
      override fun addUser(user: User) {
      return userMapper!!.addUser(user)
      }
      }
      test.html 根据数据库里perms判断什么权限显示不同界面
      在这里插入图片描述
      数据库配置(application-dev.properties)
      spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai
      spring.datasource.username=root
      spring.datasource.password=root
      rabbitmq的配置可以去掉但是空格必须和图上一样thymeleaf配置的页面
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

salt是存储的4位随机盐值
项目已上传

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值