博主分享免费Java教学视频,B站账号:Java刘哥
一套系统,除了个人网站,一般都要有多种角色,每种角色必须严格控制它的权限。
Shiro是一种轻量级的安全框架,主要是做登录验证,权限检查,相对 Spring Security 是要简单很多,源码也很清晰。
本文通过实战做一个介绍。
其他依赖就不贴,如果缺少,可以在下面评论取索取,或者在 Maven 上搜
② role 表
③ permission 表
④ user_role_ref 表
⑤ role_permission_ref 表
备注:
本来还有一张菜单表,不属于本文的范畴,就不介绍了。
跟权限有关的,表名加前缀 rbac_,项目名叫 sens,表名加前缀 sens_
Role.java
Permisison.java
UserRoleRef.java
RolePermission.java
RoleMapper.java
PermissionMapper.java
备注:
关于UserRoleRefMapper.java 和 RolePermissionRefMapper.java 本文没有涉及到,不贴了
RoleMapper.xml
PermissionMapper.xml
备注:
关于UserRoleRefMapper.xml 和 RolePermissionRefMapper.xml 本文没有涉及到,不贴了
4. Service 接口
UserService.java
RoleService.java
PermissionService.java
RoleServiceImpl.java
PermissionServiceImpl.java
上面一个方法是自定义认证方法,查询用户是否存在,然后把密码等信息放到 authenticationInfo 里,shiro 然后进行验证。
下面这个方法是被已登录用户封装角色列表和权限列表。
2.ShiroConfig.java
本文介绍至此完毕,核心部分为 ShiroRealm.java 和 ShiroConfig.java 两个类
一、准备
1.pom.xml
本文采用 SpringBoot2.0.5 + MyBatis + MyBatis Plus 下面是 Shiro 的依赖- <!--Shiro-->
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-spring</artifactId>
- <version>1.4.0</version>
- </dependency>
2.数据库
5张表 ① user 表- CREATE TABLE `sens_user` (
- `user_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
- `login_enable` varchar(255) DEFAULT NULL,
- `login_error` int(11) DEFAULT NULL,
- `login_last` datetime DEFAULT NULL,
- `user_avatar` varchar(255) DEFAULT NULL,
- `user_desc` varchar(255) DEFAULT NULL,
- `user_display_name` varchar(255) DEFAULT NULL,
- `user_email` varchar(255) DEFAULT NULL,
- `user_name` varchar(255) DEFAULT NULL,
- `user_pass` varchar(255) DEFAULT NULL,
- `user_site` varchar(255) DEFAULT NULL,
- `status` int(1) DEFAULT NULL,
- `create_time` datetime DEFAULT NULL,
- PRIMARY KEY (`user_id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
- CREATE TABLE `sens_rbac_role` (
- `id` int(11) NOT NULL,
- `role` varchar(255) DEFAULT NULL,
- `description` varchar(255) DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
- CREATE TABLE `sens_rbac_permission` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `name` varchar(255) DEFAULT NULL,
- `permission` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
- CREATE TABLE `sens_rbac_user_role_ref` (
- `user_id` int(20) DEFAULT NULL,
- `role_id` int(11) DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
- CREATE TABLE `sens_rbac_role_permission_ref` (
- `role_id` int(11) DEFAULT NULL,
- `permission_id` int(11) DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
二、基础代码(Model,Dao和Service层)
1.Model 实体
User.java- package com.liuyanzhao.sens.entity;
- import com.baomidou.mybatisplus.annotations.TableId;
- import com.baomidou.mybatisplus.annotations.TableName;
- import com.baomidou.mybatisplus.enums.IdType;
- import com.fasterxml.jackson.annotation.JsonIgnore;
- import lombok.Data;
- import javax.validation.constraints.Email;
- import javax.validation.constraints.NotBlank;
- import java.io.Serializable;
- import java.util.Date;
- /**
- * <pre>
- * 博主信息
- * </pre>
- *
- * @author : saysky
- * @date : 2017/11/14
- */
- @Data
- @TableName("sens_user")
- public class User implements Serializable {
- private static final long serialVersionUID = -5144055068797033748L;
- /**
- * 编号
- */
- @TableId(type = IdType.AUTO)
- private Long userId;
- /**
- * 用户名
- */
- @NotBlank(message = "用户名不能为空")
- // @JsonIgnore
- private String userName;
- /**
- * 显示名称
- */
- private String userDisplayName;
- /**
- * 密码
- */
- // @JsonIgnore
- private String userPass;
- /**
- * 邮箱
- */
- @Email(message = "邮箱格式不正确")
- private String userEmail;
- /**
- * 头像
- */
- private String userAvatar;
- /**
- * 说明
- */
- private String userDesc;
- /**
- * 个人主页
- */
- private String userSite;
- /**
- * 是否禁用登录
- */
- // @JsonIgnore
- private String loginEnable = "true";
- /**
- * 最后一次登录时间
- */
- // @JsonIgnore
- private Date loginLast;
- /**
- * 登录错误次数记录
- */
- // @JsonIgnore
- private Integer loginError = 0;
- /**
- * 0 正常
- * 1 禁用
- * 2 已删除
- */
- private Integer status = 0;
- /**
- * 注册时间
- */
- private Date createTime;
- }
- package com.liuyanzhao.sens.entity;
- import com.baomidou.mybatisplus.annotations.TableField;
- import com.baomidou.mybatisplus.annotations.TableName;
- import lombok.Data;
- import java.io.Serializable;
- /**
- * @author liuyanzhao
- */
- @Data
- @TableName("sens_rbac_role")
- public class Role implements Serializable {
- private static final long serialVersionUID = 2233979806802117985L;
- /**
- * ID
- */
- private Integer id;
- /**
- * 角色名称:admin,author,subscriber
- */
- private String role;
- /**
- * 描述:管理员,作者,订阅者
- */
- private String description;
- /**
- * 该角色对应的用户数量,非数据库字段
- */
- @TableField(exist = false)
- private Integer count;
- }
- package com.liuyanzhao.sens.entity;
- import com.baomidou.mybatisplus.annotations.TableName;
- import lombok.Data;
- import java.io.Serializable;
- /**
- * @author liuyanzhao
- */
- @Data
- @TableName("sens_rbac_permission")
- public class Permission implements Serializable {
- private static final long serialVersionUID = 2233979806802117985L;
- /**
- * ID
- */
- private Integer id;
- /**
- * 权限名称
- */
- private String name;
- /**
- * 权限:user:list,user:add等
- */
- private String permission;
- }
- package com.liuyanzhao.sens.entity;
- import com.baomidou.mybatisplus.annotations.TableName;
- import lombok.Data;
- import java.io.Serializable;
- @Data
- @TableName("sens_rbac_user_role_ref")
- public class UserRoleRef implements Serializable {
- private static final long serialVersionUID = -1528306395712515362L;
- /**
- * 用户Id
- */
- private Long userId;
- /**
- * 角色Id
- */
- private Integer roleId;
- }
- package com.liuyanzhao.sens.entity;
- import com.baomidou.mybatisplus.annotations.TableName;
- import lombok.Data;
- import java.io.Serializable;
- @Data
- @TableName("sens_rbac_role_permission_ref")
- public class RolePermissionRef implements Serializable {
- private static final long serialVersionUID = -4015823675140925791L;
- /**
- * 角色Id
- */
- private Integer roleId;
- /**
- * 权限Id
- */
- private Integer permissionId;
- }
2.Mapper类
UserMapper.java- package com.liuyanzhao.sens.mapper;
- import com.baomidou.mybatisplus.mapper.BaseMapper;
- import com.baomidou.mybatisplus.plugins.pagination.Pagination;
- import com.liuyanzhao.sens.entity.User;
- import org.apache.ibatis.annotations.Mapper;
- import org.apache.ibatis.annotations.Param;
- import java.util.List;
- /**
- * @author liuyanzhao
- */
- @Mapper
- public interface UserMapper extends BaseMapper<User> {
- /**
- * 查询所有
- *
- * @return 用户列表
- */
- List<User> findAll(Pagination page);
- /**
- * 根据角色Id获得用户
- *
- * @param roleId 角色Id
- * @param pagination 分页信息
- * @return 用户列表
- */
- List<User> findByRoleId(@Param("roleId") Integer roleId, Pagination pagination);
- /**
- * 根据用户名获得用户
- *
- * @param userName 用户名
- * @return 用户
- */
- User findByUserName(String userName);
- /**
- * 根据用户邮箱获得用户
- *
- * @param userEmail 邮箱
- * @return 用户
- */
- User findByUserEmail(String userEmail);
- /**
- * 根据用户Id获得用户
- * @param userId 用户Id
- * @return 用户
- */
- User findByUserId(Long userId);
- }
- package com.liuyanzhao.sens.mapper;
- import com.baomidou.mybatisplus.mapper.BaseMapper;
- import com.liuyanzhao.sens.entity.Role;
- import org.apache.ibatis.annotations.Mapper;
- import java.util.List;
- /**
- * @author liuyanzhao
- */
- @Mapper
- public interface RoleMapper extends BaseMapper<Role> {
- /**
- * 根据用户Id获得角色列表
- *
- * @param userId 用户Id
- * @return 角色列表
- */
- List<Role> findByUserId(Long userId);
- /**
- * 根据名称获得角色
- *
- * @param roleName 角色名
- * @return 角色
- */
- Role findByRoleName(String roleName);
- /**
- * 获得角色列表
- *
- * @return 角色列表
- */
- List<Role> findAll();
- /**
- * 删除用户和角色管理
- *
- * @param userId 用户ID
- * @return 影响行数
- */
- Integer deleteByUserId(Long userId);
- /**
- * 统计某个角色的用户数
- *
- * @param roleId 角色Id
- * @return 用户数
- */
- Integer countUserByRoleId(Integer roleId);
- }
- package com.liuyanzhao.sens.mapper;
- import com.baomidou.mybatisplus.mapper.BaseMapper;
- import com.liuyanzhao.sens.entity.Permission;
- import org.apache.ibatis.annotations.Mapper;
- import java.util.List;
- /**
- * @author liuyanzhao
- */
- @Mapper
- public interface PermissionMapper extends BaseMapper<Permission> {
- /**
- * 根据角色Id获得权限列表
- *
- * @param roleId 角色Id
- * @return 权限列表
- */
- List<Permission> findByRoleId(Integer roleId);
- /**
- * 获得权限列表
- *
- * @return 权限列表
- */
- List<Permission> findAll();
- }
3.Mapper xml
UserMapper.xml- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.liuyanzhao.sens.mapper.UserMapper">
- <resultMap id="BaseResultMap" type="com.liuyanzhao.sens.entity.User">
- <id column="user_id" jdbcType="INTEGER" property="userId"/>
- <result column="user_name" jdbcType="VARCHAR" property="userName"/>
- <result column="user_display_name" jdbcType="VARCHAR" property="userDisplayName"/>
- <result column="user_pass" jdbcType="VARCHAR" property="userPass"/>
- <result column="user_email" jdbcType="VARCHAR" property="userEmail"/>
- <result column="user_avatar" jdbcType="VARCHAR" property="userAvatar"/>
- <result column="user_desc" jdbcType="VARCHAR" property="userDesc"/>
- <result column="user_site" jdbcType="VARCHAR" property="userSite"/>
- <result column="login_enable" jdbcType="VARCHAR" property="loginEnable"/>
- <result column="login_last" jdbcType="TIMESTAMP" property="loginLast"/>
- <result column="login_error" jdbcType="INTEGER" property="loginError"/>
- <result column="status" jdbcType="INTEGER" property="status"/>
- <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
- </resultMap>
- <sql id="all_columns">
- user_id, user_name, user_display_name, user_pass, user_email,
- user_avatar, user_desc, user_site, login_enable, login_last,
- login_error, status, create_time
- </sql>
- <sql id="normal">0</sql>
- <sql id="all_columns_with_table_name">
- sens_user.user_id, sens_user.user_name, sens_user.user_display_name,
- sens_user.user_pass, sens_user.user_email, sens_user.user_avatar,
- sens_user.user_desc, sens_user.user_site, sens_user.login_enable,
- sens_user.login_last, sens_user.login_error, sens_user.status, sens_user.create_time
- </sql>
- <sql id="tb">`sens_user`</sql>
- <sql id="all_values">
- #{userId}, #{userName}, #{userDisplayName}, #{userEmail}, #{userPass},
- #{userAvatar}, #{userDesc}, #{userSite}, #{loginEnable}, #{loginLast},
- #{loginError}, #{status}, #{createTime}
- </sql>
- <select id="findAll" resultMap="BaseResultMap">
- SELECT
- <include refid="all_columns"/>
- FROM
- <include refid="tb"/>
- </select>
- <select id="findByRoleId" resultType="com.liuyanzhao.sens.entity.User">
- SELECT
- <include refid="all_columns_with_table_name"/>
- FROM sens_rbac_user_role_ref,
- sens_user WHERE sens_rbac_user_role_ref.role_id = #{roleId} AND
- sens_rbac_user_role_ref.user_id = sens_user.user_id
- </select>
- <select id="findByUserName" resultType="com.liuyanzhao.sens.entity.User">
- SELECT
- <include refid="all_columns"/>
- FROM
- <include refid="tb"/>
- WHERE user_name = #{value} AND status = <include refid="normal"/> LIMIT 1
- </select>
- <select id="findByUserEmail" resultType="com.liuyanzhao.sens.entity.User">
- SELECT
- <include refid="all_columns"/>
- FROM
- <include refid="tb"/>
- WHERE user_email = #{value} AND status = <include refid="normal"/> LIMIT 1
- </select>
- <select id="findByUserId" resultType="com.liuyanzhao.sens.entity.User">
- SELECT
- <include refid="all_columns"/>
- FROM
- <include refid="tb"/>
- WHERE user_id = #{value}
- </select>
- </mapper>
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.liuyanzhao.sens.mapper.RoleMapper">
- <resultMap id="BaseResultMap" type="com.liuyanzhao.sens.entity.Role">
- <id column="id" jdbcType="INTEGER" property="id"/>
- <result column="role" jdbcType="VARCHAR" property="role"/>
- <result column="description" jdbcType="VARCHAR" property="description"/>
- </resultMap>
- <sql id="all_columns">
- id, role, description
- </sql>
- <sql id="tb">`sens_rbac_role`</sql>
- <sql id="all_values">
- #{id}, #{role}, #{description}
- </sql>
- <sql id="all_values_with_table_name">
- sens_rbac_role.id, sens_rbac_role.role, sens_rbac_role.description
- </sql>
- <select id="findByUserId" resultType="com.liuyanzhao.sens.entity.Role">
- select
- <include refid="all_values_with_table_name"/>
- FROM<include refid="tb"/>, sens_rbac_user_role_ref
- where sens_rbac_user_role_ref.user_id = #{value} AND
- sens_rbac_role.id = sens_rbac_user_role_ref.role_id
- </select>
- <select id="findByRoleName" resultType="com.liuyanzhao.sens.entity.Role">
- SELECT
- <include refid="all_columns"/>
- FROM
- <include refid="tb"/>
- WHERE role = #{value} LIMIT 1
- </select>
- <select id="findAll" resultType="com.liuyanzhao.sens.entity.Role">
- SELECT
- <include refid="all_columns"/>
- FROM
- <include refid="tb"/>
- </select>
- <delete id="deleteByUserId">
- DELETE FROM sens_rbac_user_role_ref
- WHERE user_id = #{value}
- </delete>
- <select id="countUserByRoleId" resultType="java.lang.Integer">
- SELECT count(*) FROM sens_rbac_user_role_ref,sens_user
- WHERE sens_rbac_user_role_ref.role_id = #{value} AND
- sens_rbac_user_role_ref.user_id = sens_user.user_id
- </select>
- </mapper>
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.liuyanzhao.sens.mapper.PermissionMapper">
- <resultMap id="BaseResultMap" type="com.liuyanzhao.sens.entity.Permission">
- <id column="id" jdbcType="INTEGER" property="id"/>
- <result column="name" jdbcType="VARCHAR" property="name"/>
- <result column="permission" jdbcType="VARCHAR" property="permission"/>
- </resultMap>
- <sql id="all_columns">
- id, `name`, permission
- </sql>
- <sql id="all_values_with_table_name">
- sens_rbac_permission.id, sens_rbac_permission.`name`, sens_rbac_permission.permission
- </sql>
- <sql id="tb">`sens_rbac_permission`</sql>
- <sql id="all_values">
- #{id}, #{name}, #{permission}
- </sql>
- <select id="findByRoleId" resultType="com.liuyanzhao.sens.entity.Permission">
- select
- <include refid="all_values_with_table_name"/>
- FROM<include refid="tb"/>, sens_rbac_role_permission_ref
- where sens_rbac_role_permission_ref.role_id = #{value} AND
- sens_rbac_permission.id = sens_rbac_role_permission_ref.permission_id
- </select>
- <select id="findAll" resultType="com.liuyanzhao.sens.entity.Permission">
- SELECT <include refid="all_columns"/>
- FROM <include refid="tb"/>
- </select>
- </mapper>
- package com.liuyanzhao.sens.service;
- import com.baomidou.mybatisplus.plugins.Page;
- import com.liuyanzhao.sens.entity.User;
- import java.util.Date;
- /**
- * <pre>
- * 用户业务逻辑接口
- * </pre>
- *
- * @author : saysky
- * @date : 2017/11/14
- */
- public interface UserService {
- /**
- * 新增/修改用户
- *
- * @param user user
- * @return Role
- */
- User saveByUser(User user);
- /**
- * 根据用户名获得用户
- *
- * @param userName 用户名
- * @return 用户
- */
- User findByUserName(String userName);
- /**
- * 根据邮箱查找用户
- *
- * @param userEmail 邮箱
- * @return User
- */
- User findByEmail(String userEmail);
- /**
- * 根据用户Id获得用户
- *
- * @param userId 用户名
- * @return 用户
- */
- User findByUserId(Long userId);
- /**
- * 修改禁用状态
- *
- * @param enable enable
- */
- void updateUserLoginEnable(User user, String enable);
- /**
- * 修改最后登录时间
- *
- * @param lastDate 最后登录时间
- * @return User
- */
- User updateUserLoginLast(User user, Date lastDate);
- /**
- * 增加登录错误次数
- *
- * @return 登录错误次数
- */
- Integer updateUserLoginError(User user);
- /**
- * 修改用户的登录状态为正常
- *
- * @return User
- */
- User updateUserLoginNormal(User user);
- }
- package com.liuyanzhao.sens.service;
- import com.liuyanzhao.sens.entity.Role;
- import com.liuyanzhao.sens.entity.User;
- import java.util.List;
- /**
- * <pre>
- * 角色逻辑接口
- * </pre>
- *
- * @author : saysky
- * @date : 2017/11/14
- */
- public interface RoleService {
- /**
- * 新增/修改角色
- *
- * @param role role
- * @return Role
- */
- Role saveByRole(Role role);
- /**
- * 根据编号删除
- *
- * @param roleId roleId
- * @return Role
- */
- void removeByRoleId(Integer roleId);
- /**
- * 删除某个用户的所有关联
- *
- * @param userId 用户Id
- */
- void removeByUserId(Long userId);
- /**
- * 根据编号查询单个权限
- *
- * @param roleId roleId
- * @return Role
- */
- Role findByRoleId(Integer roleId);
- /**
- * 根据编号查询单个权限
- *
- * @param roleName roleName
- * @return Role
- */
- Role findByRoleName(String roleName);
- /**
- * 根据用户Id获得角色列表
- *
- * @param userId 用户Id
- * @return 角色列表
- */
- List<Role> listRolesByUserId(Long userId);
- /**
- * 获得所有的角色
- *
- * @return 角色列表
- */
- List<Role> findAll();
- }
- package com.liuyanzhao.sens.service;
- import com.liuyanzhao.sens.entity.Permission;
- import java.util.List;
- /**
- * <pre>
- * 权限逻辑接口
- * </pre>
- *
- * @author : saysky
- * @date : 2017/11/14
- */
- public interface PermissionService {
- /**
- * 新增/修改权限
- *
- * @param permission permission
- * @return Permission
- */
- Permission saveByPermission(Permission permission);
- /**
- * 根据编号删除
- *
- * @param permissionId permissionId
- * @return Permission
- */
- void removeByPermissionId(Integer permissionId);
- /**
- * 根据编号查询单个权限
- *
- * @param permissionId permissionId
- * @return Permission
- */
- Permission findByPermissionId(Integer permissionId);
- /**
- * 根据角色Id获得权限列表
- *
- * @param roleId 角色Id
- * @return 权限列表
- */
- List<Permission> listPermissionsByRoleId(Integer roleId);
- }
5.Service 实现
UserServiceImpl.java- package com.liuyanzhao.sens.service.impl;
- import com.baomidou.mybatisplus.plugins.Page;
- import com.liuyanzhao.sens.entity.Role;
- import com.liuyanzhao.sens.mapper.UserMapper;
- import com.liuyanzhao.sens.entity.User;
- import com.liuyanzhao.sens.model.enums.CommentStatusEnum;
- import com.liuyanzhao.sens.model.enums.TrueFalseEnum;
- import com.liuyanzhao.sens.model.enums.UserStatusEnum;
- import com.liuyanzhao.sens.service.RoleService;
- import com.liuyanzhao.sens.service.UserService;
- import com.liuyanzhao.sens.utils.RedisUtil;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.cache.annotation.CacheEvict;
- import org.springframework.cache.annotation.Cacheable;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import java.util.Collections;
- import java.util.Date;
- import java.util.List;
- import java.util.Objects;
- /**
- * <pre>
- * 用户业务逻辑实现类
- * </pre>
- *
- * @author : saysky
- * @date : 2017/11/14
- */
- @Service
- public class UserServiceImpl implements UserService {
- private static final String USERS_CACHE_NAME = "users";
- @Autowired(required = false)
- private UserMapper userMapper;
- @Autowired(required = false)
- private RoleService roleService;
- @Override
- @CacheEvict(value = USERS_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public User saveByUser(User user) {
- if (user != null && user.getUserId() != null) {
- userMapper.updateById(user);
- } else {
- userMapper.insert(user);
- }
- return user;
- }
- @Override
- @Cacheable(value = USERS_CACHE_NAME, key = "'users_name_'+#userName", unless = "#result == null")
- public User findByUserName(String userName) {
- return userMapper.findByUserName(userName);
- }
- @Override
- @Cacheable(value = USERS_CACHE_NAME, key = "'users_email_'+#userEmail", unless = "#result == null")
- public User findByEmail(String userEmail) {
- return userMapper.findByUserEmail(userEmail);
- }
- @Override
- @Cacheable(value = USERS_CACHE_NAME, key = "'users_id_'+#userName", unless = "#result == null")
- public User findByUserId(Long userId) {
- return userMapper.findByUserId(userId);
- }
- @Override
- @CacheEvict(value = USERS_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public void updateUser(User user) {
- userMapper.updateById(user);
- }
- /**
- * 修改禁用状态
- *
- * @param enable enable
- */
- @Override
- @CacheEvict(value = USERS_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public void updateUserLoginEnable(User user, String enable) {
- //如果是修改为正常, 重置错误次数
- if (Objects.equals(TrueFalseEnum.TRUE.getDesc(), enable)) {
- user.setLoginError(0);
- }
- user.setLoginEnable(enable);
- this.updateUser(user);
- }
- /**
- * 修改最后登录时间
- *
- * @param lastDate 最后登录时间
- * @return User
- */
- @Override
- @CacheEvict(value = USERS_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public User updateUserLoginLast(User user, Date lastDate) {
- user.setLoginLast(lastDate);
- this.updateUser(user);
- return user;
- }
- /**
- * 增加登录错误次数
- *
- * @return 登录错误次数
- */
- @Override
- @CacheEvict(value = USERS_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public Integer updateUserLoginError(User user) {
- user.setLoginError((user.getLoginError() == null ? 0 : user.getLoginError()) + 1);
- this.updateUser(user);
- return user.getLoginError();
- }
- /**
- * 修改用户的状态为正常
- *
- * @return User
- */
- @Override
- @CacheEvict(value = USERS_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public User updateUserLoginNormal(User user) {
- user.setLoginEnable(TrueFalseEnum.TRUE.getDesc());
- user.setLoginError(0);
- user.setLoginLast(new Date());
- this.updateUser(user);
- return user;
- }
- @Override
- @CacheEvict(value = USERS_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public void updateUserStatus(Long userId, Integer status) {
- User user = findByUserId(userId);
- if (user != null) {
- user.setStatus(status);
- userMapper.updateById(user);
- }
- }
- }
- package com.liuyanzhao.sens.service.impl;
- import com.liuyanzhao.sens.entity.Role;
- import com.liuyanzhao.sens.mapper.RoleMapper;
- import com.liuyanzhao.sens.service.RoleService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.cache.annotation.CacheEvict;
- import org.springframework.cache.annotation.Cacheable;
- import org.springframework.stereotype.Service;
- import java.util.List;
- /**
- * <pre>
- * 角色业务逻辑实现类
- * </pre>
- *
- * @author : saysky
- * @date : 2017/11/14
- */
- @Service
- public class RoleServiceImpl implements RoleService {
- private static final String ROLES_CACHE_NAME = "roles";
- @Autowired(required = false)
- private RoleMapper roleMapper;
- @Override
- @CacheEvict(value = ROLES_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public Role saveByRole(Role role) {
- if (role != null && role.getId() != null) {
- roleMapper.updateById(role);
- } else {
- roleMapper.insert(role);
- }
- return role;
- }
- @Override
- @CacheEvict(value = ROLES_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public void removeByRoleId(Integer roleId) {
- roleMapper.deleteById(roleId);
- }
- @Override
- @CacheEvict(value = ROLES_CACHE_NAME, allEntries = true, beforeInvocation = true)
- public void removeByUserId(Long userId) {
- roleMapper.deleteByUserId(userId);
- }
- @Override
- @Cacheable(value = ROLES_CACHE_NAME, key = "'roles_id_'+#roleId", unless = "#result == null")
- public Role findByRoleId(Integer roleId) {
- return roleMapper.selectById(roleId);
- }
- @Override
- @Cacheable(value = ROLES_CACHE_NAME, key = "'roles_name_'+#roleName", unless = "#result == null")
- public Role findByRoleName(String roleName) {
- return roleMapper.findByRoleName(roleName);
- }
- @Override
- @Cacheable(value = ROLES_CACHE_NAME, key = "'roles_uid_'+#userId", unless = "#result == null")
- public List<Role> listRolesByUserId(Long userId) {
- return roleMapper.findByUserId(userId);
- }
- @Override
- @Cacheable(value = ROLES_CACHE_NAME, key = "'roles_all'")
- public List<Role> findAll() {
- //获得角色
- List<Role> roles = roleMapper.findAll();
- //封装count
- roles.forEach(role -> role.setCount(roleMapper.countUserByRoleId(role.getId())));
- return roles;
- }
- }
- package com.liuyanzhao.sens.service;
- import com.liuyanzhao.sens.entity.Permission;
- import java.util.List;
- /**
- * <pre>
- * 权限逻辑接口
- * </pre>
- *
- * @author : saysky
- * @date : 2017/11/14
- */
- public interface PermissionService {
- /**
- * 新增/修改权限
- *
- * @param permission permission
- * @return Permission
- */
- Permission saveByPermission(Permission permission);
- /**
- * 根据编号删除
- *
- * @param permissionId permissionId
- * @return Permission
- */
- void removeByPermissionId(Integer permissionId);
- /**
- * 根据编号查询单个权限
- *
- * @param permissionId permissionId
- * @return Permission
- */
- Permission findByPermissionId(Integer permissionId);
- /**
- * 根据角色Id获得权限列表
- *
- * @param roleId 角色Id
- * @return 权限列表
- */
- List<Permission> listPermissionsByRoleId(Integer roleId);
- }
三、整合 Shiro
1.MyShiroRealm.java- package com.liuyanzhao.sens.config;
- import cn.hutool.core.date.DateUnit;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.lang.Validator;
- import cn.hutool.core.util.ObjectUtil;
- import cn.hutool.extra.servlet.ServletUtil;
- import cn.hutool.http.HtmlUtil;
- import com.liuyanzhao.sens.entity.Permission;
- import com.liuyanzhao.sens.entity.Role;
- import com.liuyanzhao.sens.entity.User;
- import com.liuyanzhao.sens.model.dto.JsonResult;
- import com.liuyanzhao.sens.model.dto.LogsRecord;
- import com.liuyanzhao.sens.model.enums.CommonParamsEnum;
- import com.liuyanzhao.sens.model.enums.ResultCodeEnum;
- import com.liuyanzhao.sens.model.enums.TrueFalseEnum;
- import com.liuyanzhao.sens.service.PermissionService;
- import com.liuyanzhao.sens.service.RoleService;
- import com.liuyanzhao.sens.service.UserService;
- import com.liuyanzhao.sens.utils.LocaleMessageUtil;
- import com.liuyanzhao.sens.utils.Md5Util;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.lang3.StringUtils;
- 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.springframework.beans.factory.annotation.Autowired;
- import java.util.Date;
- import java.util.List;
- /**
- * 默认的realm
- *
- * @author 言曌
- * @date 2018/9/1 上午10:47
- */
- @Slf4j
- public class MyShiroRealm extends AuthorizingRealm {
- @Autowired
- private UserService userService;
- @Autowired
- private RoleService roleService;
- @Autowired
- private PermissionService permissionService;
- @Autowired
- private LocaleMessageUtil localeMessageUtil;
- /**
- * 认证信息(身份验证) Authentication 是用来验证用户身份
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
- log.info("认证-->MyShiroRealm.doGetAuthenticationInfo()");
- //1.验证用户名
- User user = null;
- String loginName = (String) token.getPrincipal();
- if (Validator.isEmail(loginName)) {
- user = userService.findByEmail(loginName);
- } else {
- user = userService.findByUserName(loginName);
- }
- if (user == null) {
- //用户不存在
- log.info("用户不存在! 登录名:{}, 密码:{}", loginName, token.getCredentials());
- return null;
- }
- //2.首先判断是否已经被禁用已经是否已经过了10分钟
- Date loginLast = DateUtil.date();
- if (null != user.getLoginLast()) {
- loginLast = user.getLoginLast();
- }
- Long between = DateUtil.between(loginLast, DateUtil.date(), DateUnit.MINUTE);
- if (StringUtils.equals(user.getLoginEnable(), TrueFalseEnum.FALSE.getDesc()) && (between < CommonParamsEnum.TEN.getValue())) {
- log.info("账号已锁定! 登录名:{}, 密码:{}", loginName, token.getCredentials());
- throw new LockedAccountException("账号被锁定");
- }
- userService.updateUserLoginLast(user, DateUtil.date());
- //3.封装authenticationInfo,准备验证密码
- SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
- user, // 用户名
- user.getUserPass(), // 密码
- ByteSource.Util.bytes("sens"), // 盐
- getName() // realm name
- );
- System.out.println("realName:" + getName());
- return authenticationInfo;
- }
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- log.info("授权-->MyShiroRealm.doGetAuthorizationInfo()");
- SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
- User user = (User) principals.getPrimaryPrincipal();
- List<Role> roles = roleService.listRolesByUserId(user.getUserId());
- for (Role role : roles) {
- authorizationInfo.addRole(role.getRole());
- List<Permission> permissions = permissionService.listPermissionsByRoleId(role.getId());
- for (Permission p : permissions) {
- authorizationInfo.addStringPermission(p.getPermission());
- }
- }
- return authorizationInfo;
- }
- }
- package com.liuyanzhao.sens.config;
- import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
- import org.apache.shiro.mgt.SecurityManager;
- import org.apache.shiro.realm.Realm;
- import org.apache.shiro.spring.LifecycleBeanPostProcessor;
- import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
- import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
- import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
- import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.DependsOn;
- import java.util.ArrayList;
- import java.util.LinkedHashMap;
- import java.util.List;
- import java.util.Map;
- /**
- * @author 言曌
- * @date 2018/8/20 上午6:19
- */
- @Configuration
- public class ShiroConfig {
- @Bean
- public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
- System.out.println("ShiroConfiguration.shirFilter()");
- ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
- shiroFilterFactoryBean.setSecurityManager(securityManager);
- //拦截器.
- Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
- // 配置不会被拦截的链接 顺序判断
- filterChainDefinitionMap.put("/static/**", "anon");
- filterChainDefinitionMap.put("/upload/**", "anon");
- filterChainDefinitionMap.put("/favicon.ico", "anon");
- filterChainDefinitionMap.put("/favicon.ico", "anon");
- filterChainDefinitionMap.put("/admin/login", "anon");
- filterChainDefinitionMap.put("/admin/getLogin", "anon");
- //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
- filterChainDefinitionMap.put("/logout", "logout");
- //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
- //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
- filterChainDefinitionMap.put("/admin/**", "authc");
- filterChainDefinitionMap.put("/backup/**", "authc");
- filterChainDefinitionMap.put("/**", "anon");
- shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
- // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面
- shiroFilterFactoryBean.setLoginUrl("/admin/login");
- // 登录成功后要跳转的链接
- shiroFilterFactoryBean.setSuccessUrl("/");
- //未授权界面;
- shiroFilterFactoryBean.setUnauthorizedUrl("/403");
- return shiroFilterFactoryBean;
- }
- @Bean
- public SecurityManager securityManager() {
- DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
- securityManager.setRealm(myShiroRealm());
- return securityManager;
- }
- /**
- * 身份认证realm; (这个需要自己写,账号密码校验;权限等)
- *
- * @return MyShiroRealm
- */
- @Bean
- public MyShiroRealm myShiroRealm() {
- MyShiroRealm myShiroRealm = new MyShiroRealm();
- myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
- return myShiroRealm;
- }
- /**
- * 凭证匹配器
- * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
- * 所以我们需要修改下doGetAuthenticationInfo中的代码;
- * )
- * @return
- */
- @Bean
- public HashedCredentialsMatcher hashedCredentialsMatcher(){
- HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
- hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
- hashedCredentialsMatcher.setHashIterations(2);//散列的次数,md5(md5("xxx"))
- return hashedCredentialsMatcher;
- }
- /**
- * 开启shiro aop注解支持,不开启无法使用 shiro 的注解.
- * 使用代理方式;所以需要开启代码支持;
- * @param securityManager
- * @return
- */
- /** * Shiro生命周期处理器 * @return */
- @Bean
- public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
- return new LifecycleBeanPostProcessor();
- }
- @Bean
- @DependsOn({"lifecycleBeanPostProcessor"})
- public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
- DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
- advisorAutoProxyCreator.setProxyTargetClass(true);
- return advisorAutoProxyCreator;
- }
- @Bean
- public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
- AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
- authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
- return authorizationAttributeSourceAdvisor;
- }
- }
四、登录和退出
下面是登录的后台代码 主要是做一个 subject.login() 的操作,然后 try catch,根据抛的异常,返回指定的信息或者跳转。 这里说明一下,登录成功后,不需要添加 session,shiro 有自己的 SessionDao 创建 session,但是那个 key,可能不知道。 后台获得用户直接从 subject.getPrincipal(); 里拿,前台可以用对应的模板引擎取,或者向后台发请求获得当前用户,建议不用自己弄 session,容易出现两个 session 过期时间不同,尤其是记住密码后。1.登录代码
- /**
- * 验证登录信息
- *
- * @param loginName 登录名:邮箱/用户名
- * @param loginPwd loginPwd 密码
- * @return JsonResult JsonResult
- */
- @PostMapping(value = "/doLogin")
- @ResponseBody
- public JsonResult getLogin(@ModelAttribute("loginName") String loginName,
- @ModelAttribute("loginPwd") String loginPwd) {
- Subject subject = SecurityUtils.getSubject();
- UsernamePasswordToken token = new UsernamePasswordToken(loginName, loginPwd);
- try {
- subject.login(token);
- if (subject.isAuthenticated()) {
- //登录成功,修改登录错误次数为0
- User user = (User) subject.getPrincipal();
- userService.updateUserLoginNormal(user);
- return new JsonResult(ResultCodeEnum.SUCCESS.getCode(), "登录成功!");
- }
- } catch (UnknownAccountException e) {
- log.info("UnknownAccountException -- > 账号不存在:");
- return new JsonResult(ResultCodeEnum.FAIL.getCode(), "用户不存在!"));
- } catch (IncorrectCredentialsException e) {
- //更新失败次数
- User user;
- if (Validator.isEmail(loginName)) {
- user = userService.findByEmail(loginName);
- } else {
- user = userService.findByUserName(loginName);
- }
- if (user != null) {
- Integer errorCount = userService.updateUserLoginError(user);
- //超过五次禁用账户
- if (errorCount >= CommonParamsEnum.FIVE.getValue()) {
- userService.updateUserLoginEnable(user, TrueFalseEnum.FALSE.getDesc());
- }
- int rest = 5 - errorCount;
- return new JsonResult(ResultCodeEnum.FAIL.getCode(), "密码错误,你还可以尝试 [" + rest + "] 次!"));
- }
- } catch (LockedAccountException e) {
- log.info("LockedAccountException -- > 账号被锁定");
- return new JsonResult(ResultCodeEnum.FAIL.getCode(), "账号被锁定,十分钟后再来!");
- } catch (Exception e) {
- log.info(e.getMessage());
- }
- return new JsonResult(ResultCodeEnum.FAIL.getCode(), "发送错误!");
- }
2.退出代码
- /**
- * 退出登录
- *
- * @return 重定向到/admin/login
- */
- @GetMapping(value = "/logOut")
- public String logOut() {
- Subject subject = SecurityUtils.getSubject();
- User user = (User) subject.getPrincipal();
- subject.logout();
- log.info("用户[{}]退出登录", user.getUserName());
- return "redirect:/admin/login";
- }
七、权限控制
主要是在方法上加 @RequiresPermissions 注解或者 @RequiresRoles 注解,也可以在类上加(那就是整个类里的所有方法必须要有这个权限) 下面以标签控制器为例- package com.liuyanzhao.sens.web.controller.admin;
- import com.liuyanzhao.sens.entity.Tag;
- import com.liuyanzhao.sens.model.dto.JsonResult;
- import com.liuyanzhao.sens.model.enums.ResultCodeEnum;
- import com.liuyanzhao.sens.service.TagService;
- import com.liuyanzhao.sens.utils.LocaleMessageUtil;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.shiro.authz.annotation.RequiresPermissions;
- import org.apache.shiro.authz.annotation.RequiresRoles;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.*;
- import java.util.List;
- /**
- * <pre>
- * 后台标签管理控制器
- * </pre>
- *
- * @author : saysky
- * @date : 2017/12/10
- */
- @Slf4j
- @Controller
- @RequestMapping(value = "/admin/tag")
- public class TagController {
- @Autowired
- private TagService tagService;
- @Autowired
- private LocaleMessageUtil localeMessageUtil;
- /**
- * 渲染标签管理页面
- *
- * @return 模板路径admin/admin_tag
- */
- @GetMapping
- @RequiresPermissions("tag:list")
- public String tags(Model model) {
- List<Tag> tags = tagService.findAllTags();
- model.addAttribute("tags", tags);
- return "admin/admin_tag";
- }
- /**
- * 新增/修改标签
- *
- * @param tag tag
- * @return 重定向到/admin/tag
- */
- @PostMapping(value = "/save")
- @RequiresPermissions("tag:save")
- public String saveTag(@ModelAttribute Tag tag) {
- try {
- tagService.saveByTag(tag);
- } catch (Exception e) {
- log.error("新增/修改标签失败:{}", e.getMessage());
- }
- return "redirect:/admin/tag";
- }
- /**
- * 验证是否存在该路径
- *
- * @param tagUrl 标签路径名
- * @return true:不存在,false:已存在
- */
- @GetMapping(value = "/checkUrl")
- @ResponseBody
- @RequiresPermissions("tag:url:check")
- public JsonResult checkTagUrlExists(@RequestParam("tagUrl") String tagUrl) {
- Tag tag = tagService.findByTagUrl(tagUrl);
- if (null != tag) {
- return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.common.url-is-exists"));
- }
- return new JsonResult(ResultCodeEnum.SUCCESS.getCode(), "");
- }
- /**
- * 删除标签
- *
- * @param tagId 标签Id
- * @return JsonResult
- */
- @GetMapping(value = "/remove")
- @ResponseBody
- @RequiresPermissions("tag:delete")
- public JsonResult checkDelete(@RequestParam("tagId") Long tagId) {
- tagService.removeByTagId(tagId);
- return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.common.delete-success"));
- }
- /**
- * 跳转到修改标签页面
- *
- * @param model model
- * @param tagId 标签编号
- * @return 模板路径admin/admin_tag
- */
- @GetMapping(value = "/edit")
- @RequiresPermissions("tag:edit")
- public String toEditTag(Model model, @RequestParam("tagId") Long tagId) {
- //当前修改的标签
- Tag tag = tagService.findByTagId(tagId);
- model.addAttribute("updateTag", tag);
- //所有标签
- List<Tag> tags = tagService.findAllTags();
- model.addAttribute("tags", tags);
- return "admin/admin_tag";
- }
- }
- 微信
- 交流学习,有偿服务
- 博客/Java交流群
- 资源分享,问题解决,技术交流。群号:590480292
2019年04月07日 23:27:19
文章不错支持一下吧
2019年02月25日 09:27:38
springboot 整合shiro有Git链接吗,想学习下