首先在pom.xml中引入shiro包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
下面是表和实体类结构:
注:继承的baseBean为我的基础实体类,包含主键,操作记录等信息,根据业务逻辑自行修改
一、用户表实体类
@org.hibernate.annotations.Table(appliesTo = "pc_user", comment = "Pc端用户表")
public class PcUser extends BaseBean {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "varchar(100) COMMENT 'pc用户编号'")
private String pcUserId;
@Column(columnDefinition = "varchar(100) COMMENT '用户名'")
private String userName;
@Column(columnDefinition = "varchar(100) COMMENT '密码'")
private String passWord;
@GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
@Column(columnDefinition = "varchar(100) COMMENT '加盐'")
private String salt;
@Column(columnDefinition = "datetime COMMENT '最后登录时间'")
private Date lastLoginTime;
}
此为自动建表实体类,业务逻辑中使用时增加PcUserVO进行逻辑处理
@Data
public class PcUserVO extends BaseBean {
private String pcUserId;
private String userName;
private String passWord;
private String salt;
private Date lastLoginTime;
/**
* 用户对应的角色集合
*/
private Set<RoleVO> roles;
}
二、角色表实体类
@org.hibernate.annotations.Table(appliesTo = "role", comment = "pc端角色表")
public class Role extends BaseBean {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "varchar(100) COMMENT '角色编号'")
private String roleId;
@Column(columnDefinition = "varchar(100) COMMENT '角色名称'")
private String roleName;
@Column(columnDefinition = "varchar(100) COMMENT '角色描述'")
private String description;
}
此为自动建表实体类,业务逻辑中使用时增加RoleVO 进行逻辑处理
import com.pcframe.entity.BaseBean;
import com.pcframe.entity.Permissions;
import lombok.Data;
import java.util.Set;
@Data
public class RoleVO extends BaseBean {
private String roleId;
private String roleName;
private String description;
/**
* 角色对应权限集合
*/
private Set<Permissions> permissions;
}
三、权限表实体类
@org.hibernate.annotations.Table(appliesTo = "permissions", comment = "pc端权限表")
public class Permissions extends BaseBean {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "varchar(100) COMMENT '权限编号'")
private String permissionsId;
@Column(columnDefinition = "varchar(250) COMMENT '权限名称'")
private String permissionsName;
@Column(columnDefinition = "varchar(100) COMMENT '权限描述'")
private String description;
}
四、用户角色关系表实体类
@org.hibernate.annotations.Table(appliesTo = "pc_user_link_role", comment = "用户角色关系表 ")
public class PcUserLinkRole extends BaseBean {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "varchar(100) COMMENT 'pc用户编号'")
private String pcUserId;
@Column(columnDefinition = "varchar(100) COMMENT '角色编号'")
private String roleId;
}
五、角色权限关系表实体类
@org.hibernate.annotations.Table(appliesTo = "role_link_permissions", comment = "角色权限关系表 ")
public class RoleLinkPermissions extends BaseBean {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "varchar(100) COMMENT '角色编号'")
private String roleId;
@Column(columnDefinition = "varchar(100) COMMENT '权限编号'")
private String permissionsId;
}
下面是数据库查询角色与权限的服务实现类:
六、PcUserLoginServiceImpl
package com.pcframe.service.impl;
import com.pcframe.VO.PcUserVO;
import com.pcframe.VO.RoleVO;
import com.pcframe.entity.PcUser;
import com.pcframe.entity.Permissions;
import com.pcframe.entity.Role;
import com.pcframe.service.PcUserLoginService;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Service
public class PcUserLoginServiceImpl implements PcUserLoginService {
@Override
public PcUserVO getUserByName(String getMapByName) {
return getMapByName(getMapByName);
}
/**
* 模拟数据库查询(根据实际业务进行修改即可)
*
* @param userName 用户名
* @return PcUserVO
*/
private PcUserVO getMapByName(String userName) {
Permissions permissions1 = new Permissions();
permissions1.setId(1L);
permissions1.setPermissionsName("abc");
Permissions permissions2 = new Permissions();
permissions2.setId(2L);
permissions2.setPermissionsName("abcd");
Set<Permissions> permissionsSet = new HashSet<>();
permissionsSet.add(permissions1);
permissionsSet.add(permissions2);
RoleVO role = new RoleVO();
role.setId(1L);
role.setRoleName("admin");
role.setPermissions(permissionsSet);
Set<RoleVO> roleSet = new HashSet<>();
roleSet.add(role);
PcUserVO user = new PcUserVO();
user.setId(1L);
user.setUserName("zyx");
user.setPassWord("123456");
user.setRoles(roleSet);
Map<String, PcUserVO> map = new HashMap<>();
map.put(user.getUserName(), user);
Set<Permissions> permissionsSet1 = new HashSet<>();
permissionsSet1.add(permissions1);
RoleVO role1 = new RoleVO();
role1.setId(2L);
role1.setRoleName("user");
role1.setPermissions(permissionsSet1);
Set<RoleVO> roleSet1 = new HashSet<>();
roleSet1.add(role1);
PcUserVO user1 = new PcUserVO();
user1.setId(2L);
user1.setUserName("ccc");
user1.setPassWord("123456");
user1.setRoles(roleSet1);
map.put(user1.getUserName(), user1);
return map.get(userName);
}
}
自定义Realm用于查询用户的角色和权限信息并保存到权限管理器:
七、PcUserRealm
package com.pcframe.realm;
import com.pcframe.VO.PcUserVO;
import com.pcframe.VO.RoleVO;
import com.pcframe.entity.PcUser;
import com.pcframe.entity.Permissions;
import com.pcframe.entity.Role;
import com.pcframe.service.PcUserLoginService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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;
import org.springframework.util.StringUtils;
public class PcUserRealm extends AuthorizingRealm {
@Autowired
private PcUserLoginService pcUserLoginService;
/**
* @MethodName doGetAuthorizationInfo
* @Description 权限配置类
* @Param [principalCollection]
* @Return AuthorizationInfo
* @Author WangShiLin
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
//查询用户名称
PcUserVO pcUser = pcUserLoginService.getUserByName(name);
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (RoleVO role : pcUser.getRoles()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
//添加权限
for (Permissions permissions : role.getPermissions()) {
simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
}
}
return simpleAuthorizationInfo;
}
/**
* @MethodName doGetAuthenticationInfo
* @Description 认证配置类
* @Param [authenticationToken]
* @Return AuthenticationInfo
* @Author WangShiLin
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if (StringUtils.isEmpty(authenticationToken.getPrincipal())) {
return null;
}
//获取用户信息
String name = authenticationToken.getPrincipal().toString();
PcUserVO pcUser = pcUserLoginService.getUserByName(name);
if (pcUser == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, pcUser.getPassWord().toString(), getName());
return simpleAuthenticationInfo;
}
}
}
八、shiro的核心过滤器ShiroConfig
package com.pcframe.config;
import com.pcframe.realm.PcUserRealm;
import org.apache.shiro.mgt.SecurityManager;
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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//将自己的验证方式加入容器
@Bean
public PcUserRealm myShiroRealm() {
PcUserRealm pcUserRealm = new PcUserRealm();
return pcUserRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
/**
* anon 没有参数,表示可以匿名使用。
* authc 表示需要认证(登录)才能使用,没有参数
* authcBasic 没有参数表示httpBasic认证
* logout 表示添加shiro的默认退出
* noSessionCreation
* perms 参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
* port port[8091],当请求的url的端口不是8091是跳转到schemal://serverName:8091?url,其中schmal是协议http或https等,serverName是你访问的host,8091是url配置里port的端口,url是你访问的url里的?后面的参数。也就是默认更改端口
* rest 根据请求的方法,相当于/user/**=perms[user:method] ,其中method为post,get,delete等。
* roles 参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
* ssl 没有参数,表示安全的url请求,协议为https
* user 没有参数表示必须存在用户,当登入操作时不做检查
*/
//登出
map.put("/logout", "logout");
//对所有用户认证
map.put("/**", "authc");
//登录(身份认证失败,则跳转到登录页面的配置 没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性)
shiroFilterFactoryBean.setLoginUrl("/login");
//首页(登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。)
shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
九、Controller进行 测试
package com.pcframe.controller;
import com.pcframe.bean.ResultMessageBean;
import com.pcframe.entity.PcUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class PcUserController {
@GetMapping("/login")
public String login(PcUser pcUser) {
if (StringUtils.isEmpty(pcUser.getUserName()) || StringUtils.isEmpty(pcUser.getPassWord())) {
return "请输入用户名和密码!";
}
//用户认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
pcUser.getUserName(),
pcUser.getPassWord()
);
try {
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
// subject.checkRole("admin");
// subject.checkPermissions("query", "add");
} catch (UnknownAccountException e) {
log.error("用户名不存在!", e);
return "用户名不存在!";
} catch (AuthenticationException e) {
log.error("密码错误!", e);
return "密码错误!";
}
return "login success";
}
@RequiresRoles("admin")
@GetMapping("/admin")
public String admin() {
return "admin success!";
}
@RequiresPermissions("abc")
@GetMapping("/index")
public String index() {
return "index success!";
}
@RequiresPermissions("abcd")
@GetMapping("/abcd")
public String abcd() {
return "abcd success!";
}
@GetMapping("/abcc")
public String abc() {
return "abcc success!";
}
}
十、没有通过权限验证的异常
@ControllerAdvice
@Slf4j
public class MyShiroExceptionHandler {
@ExceptionHandler
@ResponseBody
public String ErrorHandler(AuthorizationException e) {
log.error("没有通过权限验证!", e);
return "没有通过权限验证!";
}
}