超详细Vue+Spring Boot整合Shiro前后端分离架构-Shiro后端设计(三)

超详细Vue+Spring Boot整合Shiro前后端分离架构-Shiro后端设计(三)

Laughing
2020-10-11 / 0 评论 / 1,628 阅读 / 搜一下 / 正在检测是否收录...

Shiro实战

shiro实战内容包括三个部分:(1)shiro后台表结构,用于存储shiro对应的用户、角色、权限及关联关系。(2)后端代码,及基于shiro配置用户、角色、权限及对应关系以及登录、认证。(3)前端代码,维护shiro信息及登录、认证。

这篇博文我们介绍第二部分,即后端设计

后端代码

后端数据库访问用的mybatis及lombok插件。

添加依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <!-- mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${spring.shiro.version}</version>
        </dependency>
        <dependency>
            <!--session持久化插件-->
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>${shiro.redis.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--    导入配置文件处理器    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

配置文件

主要配置数据库连接、mybatis及shiro信息。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  type-aliases-package: cc.lisen.shiro.entity
  mapper-locations: classpath:mapper/*.xml
shiro:
  user:
    loginUrl: /shiro/login/**
    unauthorizedUrl: /shiro/logout
    indexUrl: /shiro/index
    captchaEnabled: true
    captchaType: math
  redis:
    host: localhost
    port: 6379
  cookie:
    domain:
    path: /
    httpOnly: false
    maxAge: 72 #Hours,利用 cookie 免登录。
    secure: false
  session:
    expireTime: 72 #Hours
    dbSyncPeriod: 1
    validationInterval: 10
    maxSession: -1
    kickoutAfter: false
server:
  port: 8888
#配置跨域
cors:
  allowedOrigin:
    - http://localhost:8080
  allowCredentials:
    true
  allowHeaders:
    - \*
  allowMethods:
    - GET
    - POST
    - PUT
    - DELETE
    - OPTIONS
  maxAge:
    7200
  path:
    /**

mybatis

代码配置完成后,我们先生成mybatis代码,完成mybatis的操作。

我使用Free MyBatis plugin插件生成一些代码。

mybatis实体
ShiroUser.java
@Data
public class ShiroUser implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String userCode;

    private String userName;

    private String password;

    private Set<ShiroRole> shiroRoleSet;

}
ShiroRole.java
@Data
public class ShiroRole implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String roleCode;

    private String roleName;

    private Set<ShiroPermission> shiroPermissionSet;
}
ShiroPermission.java
@Data
public class ShiroPermission implements Serializable {
    private Long id;

    private String permissionCode;

    private String permissionName;

    private static final long serialVersionUID = 1L;
}
ShiroUserRole.java
@Data
public class ShiroUserRole implements Serializable {
    private Long id;

    private Long userId;

    private Long roleId;

    private static final long serialVersionUID = 1L;
}
ShiroRolePermission.java
@Data
public class ShiroRolePermission implements Serializable {
    private Long id;

    private Long roleId;

    private Long permissionId;

    private static final long serialVersionUID = 1L;
}
数据访问接口
ShiroUserMapper.java
@Mapper
public interface ShiroUserMapper {

    int deleteByPrimaryKey(Long id);

    int insert(ShiroUser record);

    int insertSelective(ShiroUser record);

    /**
     * 根据主键获取用户信息
     * @param id Id
     * @return 用户
     */
    ShiroUser selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(ShiroUser record);

    int updateByPrimaryKey(ShiroUser record);

    /**
     * 根据用户编号获取
     * @param userCode 用户编号
     * @return 用户
     */
    ShiroUser findUserByUserCode(String userCode);
}
ShiroRoleMapper.java
@Mapper
public interface ShiroRoleMapper {
    int deleteByPrimaryKey(Long id);

    int insert(ShiroRole record);

    int insertSelective(ShiroRole record);

    ShiroRole selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(ShiroRole record);

    int updateByPrimaryKey(ShiroRole record);

    /**
     * 根据用户编号获取其角色列表
     * @param userCode 用户编号
     * @return 权限列表
     */
    Set<ShiroRole> findByUserCode(String userCode);
}
ShiroPermissionMapper.java
@Mapper
public interface ShiroPermissionMapper {

    int deleteByPrimaryKey(Long id);

    int insert(ShiroPermission record);

    int insertSelective(ShiroPermission record);

    ShiroPermission selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(ShiroPermission record);

    int updateByPrimaryKey(ShiroPermission record);

    /**
     * 获取角色对应权限
     * @param roleId 角色Id
     * @return 权限集合
     */
    Set<ShiroPermission> findByRoleId(long roleId);
}
ShiroUserRoleMapper.java
@Mapper
public interface ShiroUserRoleMapper {

    int deleteByPrimaryKey(Long id);

    int insert(ShiroUserRole record);

    int insertSelective(ShiroUserRole record);

    ShiroUserRole selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(ShiroUserRole record);

    int updateByPrimaryKey(ShiroUserRole record);

}
ShiroRolePermissionMapper.java
@Mapper
public interface ShiroRolePermissionMapper {

    int deleteByPrimaryKey(Long id);

    int insert(ShiroRolePermission record);

    int insertSelective(ShiroRolePermission record);

    ShiroRolePermission selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(ShiroRolePermission record);

    int updateByPrimaryKey(ShiroRolePermission record);
}
xml文件
ShiroUserMapper.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="cc.lisen.shiro.mapper.ShiroUserMapper">
    <resultMap id="BaseResultMap" type="cc.lisen.shiro.entity.ShiroUser">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="userCode" jdbcType="VARCHAR" property="userCode"/>
        <result column="userName" jdbcType="VARCHAR" property="userName"/>
        <result column="password" jdbcType="VARCHAR" property="password"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, userCode, userName, `password`
    </sql>

    <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from Shiro_User
        where id = #{id,jdbcType=BIGINT}
    </select>

    <select id="findUserByUserCode" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from Shiro_User where userCode = #{userCode}
    </select>

    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
        delete
        from Shiro_User
        where id = #{id,jdbcType=BIGINT}
    </delete>
    <insert id="insert" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroUser"
            useGeneratedKeys="true">
        insert into Shiro_User (userCode, userName, `password`)
        values (#{usercode,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR})
    </insert>
    <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroUser"
            useGeneratedKeys="true">
        insert into Shiro_User
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="userCode != null">
                userCode,
            </if>
            <if test="userName != null">
                userName,
            </if>
            <if test="password != null">
                `password`,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="userCode != null">
                #{usercode,jdbcType=VARCHAR},
            </if>
            <if test="userName != null">
                #{username,jdbcType=VARCHAR},
            </if>
            <if test="password != null">
                #{password,jdbcType=VARCHAR},
            </if>
        </trim>
    </insert>
    <update id="updateByPrimaryKeySelective" parameterType="cc.lisen.shiro.entity.ShiroUser">
        update Shiro_User
        <set>
            <if test="userCode != null">
                userCode = #{usercode,jdbcType=VARCHAR},
            </if>
            <if test="userName != null">
                userName = #{username,jdbcType=VARCHAR},
            </if>
            <if test="password != null">
                `password` = #{password,jdbcType=VARCHAR},
            </if>
        </set>
        where id = #{id,jdbcType=BIGINT}
    </update>
    <update id="updateByPrimaryKey" parameterType="cc.lisen.shiro.entity.ShiroUser">
        update Shiro_User
        set userCode   = #{usercode,jdbcType=VARCHAR},
            userName   = #{username,jdbcType=VARCHAR},
            `password` = #{password,jdbcType=VARCHAR}
        where id = #{id,jdbcType=BIGINT}
    </update>
</mapper>
ShiroRoleMapper.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="cc.lisen.shiro.mapper.ShiroRoleMapper">
  <resultMap id="BaseResultMap" type="cc.lisen.shiro.entity.ShiroRole">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="roleCode" jdbcType="VARCHAR" property="roleCode" />
    <result column="roleName" jdbcType="VARCHAR" property="roleName" />
  </resultMap>
  <sql id="Base_Column_List">
    id, roleCode, roleName
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from Shiro_Role
    where id = #{id,jdbcType=BIGINT}
  </select>

  <select id="findByUserCode" resultType="cc.lisen.shiro.entity.ShiroRole">
    select Shiro_Role.* from Shiro_Role inner join Shiro_User_Role SUR on Shiro_Role.id = SUR.roleId
    inner join Shiro_User SU on SUR.userId = SU.id
    where su.userCode = #{userCode}
  </select>

  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    delete from Shiro_Role
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroRole" useGeneratedKeys="true">
    insert into Shiro_Role (roleCode, roleName)
    values (#{rolecode,jdbcType=VARCHAR}, #{rolename,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroRole" useGeneratedKeys="true">
    insert into Shiro_Role
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="roleCode != null">
        roleCode,
      </if>
      <if test="roleName != null">
        roleName,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="roleCode != null">
        #{rolecode,jdbcType=VARCHAR},
      </if>
      <if test="roleName != null">
        #{rolename,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="cc.lisen.shiro.entity.ShiroRole">
    update Shiro_Role
    <set>
      <if test="roleCode != null">
        roleCode = #{rolecode,jdbcType=VARCHAR},
      </if>
      <if test="roleName != null">
        roleName = #{rolename,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="cc.lisen.shiro.entity.ShiroRole">
    update Shiro_Role
    set roleCode = #{rolecode,jdbcType=VARCHAR},
      roleName = #{rolename,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>
</mapper>
ShiroPermissionMapper.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="cc.lisen.shiro.mapper.ShiroPermissionMapper">
    <resultMap id="BaseResultMap" type="cc.lisen.shiro.entity.ShiroPermission">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="permissionCode" jdbcType="VARCHAR" property="permissionCode"/>
        <result column="permissionName" jdbcType="VARCHAR" property="permissionName"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, permissionCode, permissionName
    </sql>
    <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from Shiro_Permission
        where id = #{id,jdbcType=BIGINT}
    </select>

    <select id="findByRoleId" resultType="cc.lisen.shiro.entity.ShiroPermission">
        select Shiro_Permission.*
        from Shiro_Permission
                 inner join Shiro_Role_Permission SRP on Shiro_Permission.id = SRP.permissionId
        where SRP.roleId = #{roleId}
    </select>

    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
        delete
        from Shiro_Permission
        where id = #{id,jdbcType=BIGINT}
    </delete>
    <insert id="insert" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroPermission"
            useGeneratedKeys="true">
        insert into Shiro_Permission (permissionCode, permissionName)
        values (#{permissioncode,jdbcType=VARCHAR}, #{permissionname,jdbcType=VARCHAR})
    </insert>
    <insert id="insertSelective" keyColumn="id" keyProperty="id"
            parameterType="cc.lisen.shiro.entity.ShiroPermission" useGeneratedKeys="true">
        insert into Shiro_Permission
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="permissionCode != null">
                permissionCode,
            </if>
            <if test="permissionName != null">
                permissionName,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="permissionCode != null">
                #{permissioncode,jdbcType=VARCHAR},
            </if>
            <if test="permissionName != null">
                #{permissionname,jdbcType=VARCHAR},
            </if>
        </trim>
    </insert>
    <update id="updateByPrimaryKeySelective" parameterType="cc.lisen.shiro.entity.ShiroPermission">
        update Shiro_Permission
        <set>
            <if test="permissionCode != null">
                permissionCode = #{permissioncode,jdbcType=VARCHAR},
            </if>
            <if test="permissionName != null">
                permissionName = #{permissionname,jdbcType=VARCHAR},
            </if>
        </set>
        where id = #{id,jdbcType=BIGINT}
    </update>
    <update id="updateByPrimaryKey" parameterType="cc.lisen.shiro.entity.ShiroPermission">
        update Shiro_Permission
        set permissionCode = #{permissioncode,jdbcType=VARCHAR},
            permissionName = #{permissionname,jdbcType=VARCHAR}
        where id = #{id,jdbcType=BIGINT}
    </update>
</mapper>
ShiroUserRoleMapper.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="cc.lisen.shiro.mapper.ShiroUserRoleMapper">
  <resultMap id="BaseResultMap" type="cc.lisen.shiro.entity.ShiroUserRole">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="userId" jdbcType="BIGINT" property="userId" />
    <result column="roleId" jdbcType="BIGINT" property="roleId" />
  </resultMap>
  <sql id="Base_Column_List">
    id, userId, roleId
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from Shiro_User_Role
    where id = #{id,jdbcType=BIGINT}
  </select>

  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    delete from Shiro_User_Role
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroUserRole" useGeneratedKeys="true">
    insert into Shiro_User_Role (userId, roleId)
    values (#{userid,jdbcType=BIGINT}, #{roleid,jdbcType=BIGINT})
  </insert>
  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroUserRole" useGeneratedKeys="true">
    insert into Shiro_User_Role
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        userId,
      </if>
      <if test="roleId != null">
        roleId,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="userid != null">
        #{userid,jdbcType=BIGINT},
      </if>
      <if test="roleId != null">
        #{roleid,jdbcType=BIGINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="cc.lisen.shiro.entity.ShiroUserRole">
    update Shiro_User_Role
    <set>
      <if test="userId != null">
        userId = #{userid,jdbcType=BIGINT},
      </if>
      <if test="roleId != null">
        roleId = #{roleid,jdbcType=BIGINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="cc.lisen.shiro.entity.ShiroUserRole">
    update Shiro_User_Role
    set userId = #{userid,jdbcType=BIGINT},
      roleId = #{roleid,jdbcType=BIGINT}
    where id = #{id,jdbcType=BIGINT}
  </update>
</mapper>
ShiroRolePermissionMapper.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="cc.lisen.shiro.mapper.ShiroRolePermissionMapper">
  <resultMap id="BaseResultMap" type="cc.lisen.shiro.entity.ShiroRolePermission">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="roleId" jdbcType="BIGINT" property="roleId" />
    <result column="permissionId" jdbcType="BIGINT" property="permissionId" />
  </resultMap>
  <sql id="Base_Column_List">
    id, roleId, permissionId
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from Shiro_Role_Permission
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    delete from Shiro_Role_Permission
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroRolePermission" useGeneratedKeys="true">
    insert into Shiro_Role_Permission (roleId, permissionId)
    values (#{roleid,jdbcType=BIGINT}, #{permissionid,jdbcType=BIGINT})
  </insert>
  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="cc.lisen.shiro.entity.ShiroRolePermission" useGeneratedKeys="true">
    insert into Shiro_Role_Permission
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="roleId != null">
        roleId,
      </if>
      <if test="permissionId != null">
        permissionId,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="roleId != null">
        #{roleid,jdbcType=BIGINT},
      </if>
      <if test="permissionId != null">
        #{permissionid,jdbcType=BIGINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="cc.lisen.shiro.entity.ShiroRolePermission">
    update Shiro_Role_Permission
    <set>
      <if test="roleId != null">
        roleId = #{roleid,jdbcType=BIGINT},
      </if>
      <if test="permissionId != null">
        permissionId = #{permissionid,jdbcType=BIGINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="cc.lisen.shiro.entity.ShiroRolePermission">
    update Shiro_Role_Permission
    set roleId = #{roleid,jdbcType=BIGINT},
      permissionId = #{permissionid,jdbcType=BIGINT}
    where id = #{id,jdbcType=BIGINT}
  </update>
</mapper>
服务层

服务层这里忽略了接口,有需要的可以查看源代码

ShiroUserServiceImpl.java
@Service
public class ShiroUserServiceImpl implements ShiroUserService {

    @Resource
    ShiroUserMapper shiroUserMapper;

    @Resource
    ShiroRoleService shiroRoleService;

    /**
     * 根据用户编号获取
     *
     * @param userCode 用户编号
     * @return 用户
     */
    @Override
    public ShiroUser findUserByUserCode(String userCode) {
        ShiroUser shiroUser = shiroUserMapper.findUserByUserCode(userCode);
        Set<ShiroRole> shiroRoleSet = shiroRoleService.findByUserCode(userCode);
        shiroUser.setShiroRoleSet(shiroRoleSet);
        return shiroUser;
    }
}
ShiroRoleServiceImpl.java
@Service
public class ShiroRoleServiceImpl implements ShiroRoleService {

    @Resource
    ShiroRoleMapper shiroRoleMapper;

    @Resource
    ShiroPermissionService shiroPermissionService;

    /**
     * 根据用户编号获取其角色列表
     *
     * @param userCode 用户编号
     * @return 权限列表
     */
    @Override
    public Set<ShiroRole> findByUserCode(String userCode) {
        Set<ShiroRole> shiroRoleSet = shiroRoleMapper.findByUserCode(userCode);
        for (ShiroRole shiroRole : shiroRoleSet) {
            Set<ShiroPermission> shiroPermissionSet = shiroPermissionService.findByRoleId(shiroRole.getId());
            shiroRole.setShiroPermissionSet(shiroPermissionSet);
        }
        return shiroRoleSet;
    }
}
ShiroPermissionServiceImpl.java
@Service
public class ShiroPermissionServiceImpl implements ShiroPermissionService {

    @Resource
    ShiroPermissionMapper shiroPermissionMapper;

    /**
     * 获取角色对应权限
     *
     * @param roleId 角色Id
     * @return 权限集合
     */
    @Override
    public Set<ShiroPermission> findByRoleId(long roleId) {
        return shiroPermissionMapper.findByRoleId(roleId);
    }
}
ShiroServiceImpl.java
@Service
public class ShiroServiceImpl implements ShiroService {

    @Resource
    ShiroUserService shiroUserService;

    /**
     * 根据用户编号获取
     *
     * @param userCode 用户编号
     * @return 用户
     */
    @Override
    public ShiroUser findUserByCode(String userCode) {
        return shiroUserService.findUserByUserCode(userCode);
    }
}

shiro配置

常量配置

AuthenConst常量类,用于配置加密方式、盐、加密次数等信息。

/**
 * @author laughing
 * @date 2020/10/12
 * @site https://lisen.cc
 */
public class AuthenConst {

    public static final String SALT = "LiSen";
    public static final String HASH_ALGORITHM_NAME = "MD5";
    public static final int HASH_INTERACTIONS = 1024;
    public static final String OPTION_REQUEST_NAME = "OPTIONS";
}
property映射
/**
 * @author laughing
 * @date 2020/10/11
 * @site https://lisen.cc
 */
@ConfigurationProperties(prefix = "shiro")
@Configuration
@Data
public class ShiroProperty {

    private User user;
    private Redis redis;

}

@Data
class User{
    private String loginUrl;
    private String unauthorizedUrl;
    private String indexUrl;
    private boolean captchaEnabled;
    private String captchaType;
}

@Data
class Redis{
    private String host;
    private long port;
}
自定义realm
/**
 * @author laughing
 * @date 2020/10/11
 * @site https://www.lisen.org
 */
public class MyAuthenRealm extends AuthorizingRealm {

    @Resource
    ShiroService shiroService;

    /**
     * 认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String userCode = token.getUsername();
        ShiroUser shiroUser = shiroService.findUserByCode(userCode);
        if (shiroUser == null) {
            throw new AuthenticationException("账户不存在");
        }
        //自定义盐值
//        ByteSource salt = ByteSource.Util.bytes(AuthenConst.SALT);
//        String password = new SimpleHash(AuthenConst.HASH_ALGORITHM_NAME, new String(token.getPassword()), AuthenConst.SALT, AuthenConst.HASH_INTERACTIONS).toHex();
        if (!new String(token.getPassword()).equals(shiroUser.getPassword())) {
            throw new IncorrectCredentialsException("账户密码不正确");
        }
//        Subject subject = SecurityUtils.getSubject();
//        ShiroUserVO shiroUserVO = new ShiroUserVO();
//        BeanUtils.copyProperties(shiroUserVO,shiroUser);
//        shiroUserVO.setSessionId(subject.getSession().getId().toString());
        return new SimpleAuthenticationInfo(userCode, shiroUser.getPassword(), getName());
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        if (StringUtils.isEmpty(principalCollection)) {
            return null;
        }
        String userCode = principalCollection.getPrimaryPrincipal().toString();
        ShiroUser shiroUser = shiroService.findUserByCode(userCode);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        for (ShiroRole shiroRole : shiroUser.getShiroRoleSet()) {
            simpleAuthorizationInfo.addRole(shiroRole.getRoleCode());
            for (ShiroPermission shiroPermission : shiroRole.getShiroPermissionSet()) {
                simpleAuthorizationInfo.addStringPermission(shiroPermission.getPermissionCode());
            }
        }
        return simpleAuthorizationInfo;
    }
}
禁止未登录跳转

因为我们是前后端分离项目,如果未登陆跳转的话,前端无法捕捉重定向后的消息,所以我们需要配置禁止未登录跳转。

/**
 * 对于跨域的POST请求,浏览器发起POST请求前都会发送一个OPTIONS请求已确定服务器是否可用,
 * OPTIONS请求通过后继续执行POST请求,而shiro自带的权限验证是无法处理OPTIONS请求的,
 * 所以这里需要重写isAccessAllowed方法
 * @author laughing
 * @date 2020/10/12
 * @site https://lisen.cc
 */
public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {
    private static final Logger log = LoggerFactory.getLogger(ShiroFormAuthenticationFilter.class);

    /**
     * 重写是否允许访问
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        if(AuthenConst.OPTION_REQUEST_NAME.equals(httpServletRequest.getMethod())){
            return true;
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws JSONException {
        PrintWriter out = null;
        HttpServletResponse res = (HttpServletResponse) response;
        try {
            res.setCharacterEncoding("UTF-8");
            res.setContentType("application/json");
            out = response.getWriter();
            out.println("未授权");
        } catch (Exception e) {
        } finally {
            if (null != out) {
                out.flush();
                out.close();
            }
        }
        return false;
    }
}
shiro配置
/**
 * @author laughing
 * @date 2020/10/11
 * @site https://lisen.cc
 */
@Configuration
public class ShiroConfig {

    /**
     * 自定义验证
     * @return
     */
    @Bean
    MyAuthenRealm myAuthenRealm() {
        MyAuthenRealm myAuthenRealm = new MyAuthenRealm();
//        myAuthenRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myAuthenRealm;
    }

    @Resource
    ShiroProperty shiroProperty;

    /**
     * 权限管理,配置主要是Realm的管理认证
     *
     * @return
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(myAuthenRealm());
        // 自定义session管理 使用redis
        defaultWebSecurityManager.setSessionManager(sessionManager());
        return defaultWebSecurityManager;
    }

    /**
     * Filter工厂,设置对应的过滤条件和跳转条件
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        Map<String, String> filterChainDefinitionMap = new HashMap<>();
        //登出
        filterChainDefinitionMap.put(shiroProperty.getUser().getUnauthorizedUrl(), "logout");
        //登录
        shiroFilterFactoryBean.setLoginUrl(shiroProperty.getUser().getLoginUrl());
        //首页
        shiroFilterFactoryBean.setSuccessUrl(shiroProperty.getUser().getIndexUrl());
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put(shiroProperty.getUser().getLoginUrl(), "anon");
        filterChainDefinitionMap.put(shiroProperty.getUser().getUnauthorizedUrl(), "anon");
        filterChainDefinitionMap.put("/error", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        LinkedHashMap<String, Filter> filtsMap = new LinkedHashMap<>();
        shiroFilterFactoryBean.setFilters(filtsMap);
        // 这里使用自定义的filter,禁止未登陆跳转
        filtsMap.put("authc", new ShiroFormAuthenticationFilter());
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 加入注解的使用,不加入这个注解不生效
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

//    @Bean
//    public HashedCredentialsMatcher hashedCredentialsMatcher() {
//        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//        //指定加密方式
//        credentialsMatcher.setHashAlgorithmName(AuthenConst.HASH_ALGORITHM_NAME);
//        //加密次数
//        credentialsMatcher.setHashIterations(AuthenConst.HASH_INTERACTIONS);
//        //此处的设置,true加密用的hex编码,false用的base64编码
//        credentialsMatcher.setStoredCredentialsHexEncoded(true);
//        return credentialsMatcher;
//    }

    @Bean
    public SessionManager sessionManager(){
        MySessionManager mySessionManager = new MySessionManager();
//        取消登陆跳转URL后面的jsessionid参数
        mySessionManager.setSessionIdUrlRewritingEnabled(false);
        mySessionManager.setSessionDAO(redisSessionDAO());
//        不过期
        mySessionManager.setGlobalSessionTimeout(-1);
        return mySessionManager;
    }

    @Bean
    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(shiroProperty.getRedis().getHost()+":"+shiroProperty.getRedis().getPort());
        redisManager.setDatabase(0);
        return redisManager;
    }

    @Bean
    public RedisSessionDAO redisSessionDAO(){
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }
}
全局异常
@RestControllerAdvice
public class MyAuthorizationException {

    @ExceptionHandler(UnauthorizedException.class)
    public String authorization(UnauthorizedException myAuthorizationException) {
        return "没有"+myAuthorizationException.toString()+"权限";
    }

}
测试类
/**
 * @author laughing
 * @date 2020/10/11
 * @site https://www.lisen.org
 */
@RestController
@RequestMapping("/shiro")
public class ShiroController {

    private final Logger logger = LoggerFactory.getLogger(ShiroController.class);

    @Resource
    ShiroService shiroService;

    @RequestMapping("/findUserByUserCode")
    public ShiroUser findUserByUserCode() {
        String userCode = "lisen";
        ShiroUser shiroUser = shiroService.findUserByCode(userCode);
        return shiroUser;
    }

    @RequestMapping("/login")
    public String login(@RequestParam("userCode") String userCode, @RequestParam("password") String password) {
//        password = new SimpleHash(AuthenConst.HASH_ALGORITHM_NAME,password,AuthenConst.SALT,AuthenConst.HASH_INTERACTIONS).toHex();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userCode, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            //进行验证,这里可以捕获异常,然后返回对应信息
            subject.login(usernamePasswordToken);
        } catch (UnknownAccountException e) {
            logger.error("用户名不存在!", e);
            return "用户名不存在!";
        } catch (AuthenticationException e) {
            logger.error("账号或密码错误!", e);
            return "账号或密码错误!";
        } catch (AuthorizationException e) {
            logger.error("没有权限!", e);
            return "没有权限";
        }
        return "login success";
    }

    @RequestMapping("/index")
    public String index(String userCode, String password) {
        return "index";
    }

    @RequiresPermissions("permission")
    @RequestMapping("/permission")
    public String permission() {
        return "permission";
    }

    @RequiresPermissions("dept:add")
    @RequestMapping("/deptadd")
    public String deptAdd() {
        return "dept:add";
    }

    @RequestMapping("/nopermission")
    public String noPermission() {
        return "noPermission";
    }

}

表数据

因为我们这里还没设计授权的界面,所以临时在数据库插入了测试数据

Shiro_User
INSERT INTO `shiro`.`Shiro_User`(`id`, `userCode`, `userName`, `password`) VALUES (1, 'lisen', '李森', 'f5e617c6615d53ae33b9d80a2087e264');
Shiro_Role
INSERT INTO `shiro`.`Shiro_Role`(`id`, `roleCode`, `roleName`) VALUES (1, 'admin', '管理员');
Shiro_Permission
INSERT INTO `shiro`.`Shiro_Permission`(`id`, `permissionCode`, `permissionName`) VALUES (1, 'dept:add', '部门-增加');
Shiro_User_Role
INSERT INTO `shiro`.`Shiro_User_Role`(`id`, `userId`, `roleId`) VALUES (1, 1, 1);
Shiro_Role_Permission
INSERT INTO `shiro`.`Shiro_Role_Permission`(`id`, `roleId`, `permissionId`) VALUES (1, 1, 1);
0

评论 (0)

取消
  1. 头像
    dddd
    MacOS · Google Chrome

    谢谢分享

    回复