spring boot 前后端分离整合shiro(二)自定义realm
想使用shiro我们需要自定义一个realm,先看看shiro relam的继承关系
我们关注两个类,一个是AuthenticatingRealm,用来认证;一个是AuthorizingRealm,用来授权。然后AuthorizingRealm又继承了AuthenticatingRealm,所以我们只要继承AuthorizingRealm就既有授权又有认证了。
继承之后需要实现两个方法,分别用于授权和认证(这两个方法名字很像,不要搞混了),然后就在这两个方法里写我们自己的认证授权。
修改实体类
用户拥有角色,角色拥有权限,而用户又可以直接拥有某个权限,所以给UserInfo添加两个list。顺便序列化,添加序列化id,之后继承redis、配置remenberMe需要。
UserInfo
package com.example.demo.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class UserInfo implements Serializable {
private static final long serialVersionUID = -482049523211701500L;
private Integer userId;
private String userName;
private String loginName;
private String password;
private Integer gender;
private List<Role> roleList;
private List<Permission> permissionList;
}
Role
package com.example.demo.entity;
import java.util.List;
public class Role {
private Integer roleId;
private String roleName;
private String description;
private List<Permission> permissionList;
}
编写UserInfoService
public interface UserInfoService {
/**
* 查询用户的基本信息,不包括角色、权限信息
* @param loginName
* @return
*/
UserInfo findUserInfoByLoginName(String loginName);
/**
* 查询用户的信息,包括角色、权限信息
* @param userInfo
* @return
*/
UserInfo findUserAllInfoByUserId(UserInfo userInfo);
}
impl
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private PermissionMapper permissionMapper;
@Override
public UserInfo findUserInfoByLoginName(String loginName) {
return userInfoMapper.selectUserInfoByLoginName(loginName);
}
@Override
public UserInfo findUserAllInfoByUserId(UserInfo userInfo) {
userInfo.setRoleList(roleMapper.selectRolesByUserId(userInfo.getUserId()));
userInfo.setPermissionList(permissionMapper.selectPermissionsByUserId(userInfo.getUserId()));
return userInfo;
}
}
部分mapper代码
userInfoMapper
<resultMap id="MyResultMap" type="com.example.demo.entity.UserInfo" >
<id column="user_id" property="userId" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="login_name" property="loginName" jdbcType="VARCHAR" />
<result column="password" property="password" jdbcType="VARCHAR" />
<result column="status" property="status" jdbcType="INTEGER" />
<collection property="roleList" ofType="com.example.demo.entity.Role">
<id column="role_id" property="roleId" jdbcType="INTEGER" />
<result column="role_name" property="roleName" jdbcType="VARCHAR" />
<result column="description" property="description" jdbcType="VARCHAR" />
</collection>
<collection property="permissionList" ofType="com.example.demo.entity.Permission">
<id column="pms_id" property="pmsId" jdbcType="INTEGER" />
<result column="url" property="url" jdbcType="VARCHAR" />
<result column="pms_name" property="pmsName" jdbcType="VARCHAR" />
</collection>
</resultMap>
<select id="selectUserInfoByLoginName" resultMap="MyResultMap">
select * from user_info u
where u.login_name = #{loginName};
</select>
roleMapper
<resultMap id="MyResultMap" type="com.example.demo.entity.Role" >
<id column="role_id" property="roleId" jdbcType="INTEGER" />
<result column="role_name" property="roleName" jdbcType="VARCHAR" />
<result column="description" property="description" jdbcType="VARCHAR" />
<collection property="permissionList" ofType="com.example.demo.entity.Permission">
<id column="pms_id" property="pmsId" jdbcType="INTEGER" />
<result column="url" property="url" jdbcType="VARCHAR" />
<result column="description" property="description" jdbcType="VARCHAR" />
<result column="pms_name" property="pmsName" jdbcType="VARCHAR" />
</collection>
</resultMap>
<select id="selectRolesByUserId" resultMap="MyResultMap" parameterType="java.lang.Integer" >
select r.*, p.*
from user_role ur
left join role r on ur.role_id = r.role_id
left join role_permission rp on r.role_id = rp.role_id
left join permission p on rp.pms_id = p.pms_id
where ur.user_id = #{userID};
</select>
然后完善realm
首先是foGetAuthenticationInfo,认证方法
关注两个对象,一个是参数 authenticationToken 这个token就是由subject传递过来的用户输入的账号密码,可以从里面拿到loginName。 第二个SimpleAuthenticationInfo对象,代表用户的信息,这里只把查询到的结果返回交给shiro管理,密码比较等操作由shiro去完成。
完整的relam:
package com.example.demo.config;
import com.example.demo.entity.Permission;
import com.example.demo.entity.Role;
import com.example.demo.entity.UserInfo;
import com.example.demo.service.UserInfoService;
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.springframework.beans.factory.annotation.Autowired;
/**
* @author 黄豪琦
* 日期:2019-07-09 11:14
* 说明:
*/
public class CustomizeRealm extends AuthorizingRealm {
@Autowired
private UserInfoService userInfoService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从shiro中拿到保存的对象
UserInfo var1 = (UserInfo)principalCollection.getPrimaryPrincipal();
//从数据库中将角色、权限信息查询出
UserInfo var2 = userInfoService.findUserAllInfoByUserId(var1);
//保存用户角色、权限信息的对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (Role item: var2.getRoleList()) {
//添加角色
info.addRole(item.getRoleName());
for (Permission pmsItem: item.getPermissionList()) {
//添加角色所拥有的权限
info.addStringPermission(pmsItem.getPmsName());
}
}
for (Permission item: var2.getPermissionList()) {
//添加用户直接拥有的权限
info.addStringPermission(item.getPmsName());
}
return info;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String loginName = (String)authenticationToken.getPrincipal();
UserInfo user = userInfoService.findUserInfoByLoginName(loginName);
if(null == user){
throw new UnknownAccountException("用户不存在或密码错误");
}
return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
}
}