今天学了一下shiro安全框架根据别人的文章实验成功 特地记录一下
原文转自:https://www.jianshu.com/p/7f724bec3dc3
我用的是springboot框架集成的
1 项目架构
2 导入依赖
<!-- shiro安全框架-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
3 创建实体类
3.1 用户类
//用户对应的实体类
@Data
@EqualsAndHashCode
@AllArgsConstructor
public class User implements Serializable {
private String id;
private String userName;
private String password;
/**
* 用户对应的角色集合
*/
private Set<Role> roles;
}
3.2 角色类
//角色对应的实体类
@Data
@EqualsAndHashCode
@AllArgsConstructor
public class Role implements Serializable {
private String id;
private String roleName;
/**
* 角色对应权限集合
*/
private Set<Permissions> permissions;
}
3.3 权限类
//权限对应的实体类
@Data
@EqualsAndHashCode
@AllArgsConstructor
public class Permissions implements Serializable {
private String id;
private String permissionsName;
}
4 开始编写逻辑层
4.1 接口
User getUserByName(String getMapByName);
4.2 继承类
添加了两个角色:
wsl 123456 角色:admin 权限:add query
zhangsan 123456 角色:user 权限:query
//模拟从数据库查询数据
@Override
public User getUserByName(String getMapByName) {
//模拟数据库查询,正常情况此处是从数据库或者缓存查询。
return getMapByName(getMapByName);
}
/**
* 模拟数据库查询
* @param userName
* @return
*/
private User getMapByName(String userName){
//共添加两个用户,两个用户都是admin一个角色,
//wsl有query和add权限,zhangsan只有一个query权限
Permissions permissions1 = new Permissions("1","query");
Permissions permissions2 = new Permissions("2","add");
Set<Permissions> permissionsSet = new HashSet<>();
permissionsSet.add(permissions1);
permissionsSet.add(permissions2);
Role role = new Role("1","admin",permissionsSet);
Set<Role> roleSet = new HashSet<>();
roleSet.add(role);
User user = new User("1","wsl","123456",roleSet);
Map<String ,User> map = new HashMap<>();
map.put(user.getUserName(), user);
Permissions permissions3 = new Permissions("3","query");
Set<Permissions> permissionsSet1 = new HashSet<>();
permissionsSet1.add(permissions3);
Role role1 = new Role("2","user",permissionsSet1);
Set<Role> roleSet1 = new HashSet<>();
roleSet1.add(role1);
User user1 = new User("2","zhangsan","123456",roleSet1);
map.put(user1.getUserName(), user1);
return map.get(userName);
}
5 开始写shiro的配置文件 在config包里面写一个ShiroConfig 配置类
把CustomRealm和SecurityManager等加入到spring容器
//shiro安全框架的配置类
@Configuration
public class ShiroConfig {
//不加这个注解不生效,具体不详
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//将自己的验证方式加入容器
@Bean
public CustomRealm myShiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
//权限管理,配置主要是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<>();
//登出
map.put("/logout", "logout");
//对所有用户认证
map.put("/**", "authc");
//登录
shiroFilterFactoryBean.setLoginUrl("/login");
//首页
shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//加入注解的使用,不加入这个注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
//shiro 的过滤器
//@Bean(name = "shiroFilter")
//public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
// ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// shiroFilterFactoryBean.setSecurityManager( securityManager);
// //设置登陆页面
// shiroFilterFactoryBean.setLoginUrl("/login");
// //权限不足跳转页面
// shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
//
// Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
// filterChainDefinitionMap.put("/webjars/**", "anon");
// filterChainDefinitionMap.put("/login", "anon");
// filterChainDefinitionMap.put("/", "anon");
// filterChainDefinitionMap.put("/front/**", "anon");
// filterChainDefinitionMap.put("/api/**", "anon");
//
// filterChainDefinitionMap.put("/admin/**", "authc");
// filterChainDefinitionMap.put("/user/**", "authc");
// //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
// filterChainDefinitionMap.put("/**", "authc");
// shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// return shiroFilterFactoryBean;
//
//}
}
6 写CustomRealm类 在shiro包下 自定义Realm用于查询用户的角色和权限信息并保存到权限管理器
public class CustomRealm extends AuthorizingRealm {
@Resource
private DemoService demoService;
// 权限认证,即登录过后,每个身份不一定,对应的所能看的页面也不一样。
//使用shiro时,如果正常登陆(执行subject.login(token)成功)就能在全局通过SecurityUtils.getSubject().getPrincipal()获取用户信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取用户登陆名
String username = (String) SecurityUtils.getSubject().getPrincipal();
//根据用户登录名 查询用户信息
User user = demoService.getUserByName(username);
//添加角色权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//先遍历用户的角色
for (Role role : user.getRoles()) {
//添加角色
info.addRole(role.getRoleName());
//添加权限
for (Permissions permission : role.getPermissions()) {
info.addStringPermission(permission.getPermissionsName());
}
}
//Set<String> stringSet = new HashSet<>();
//stringSet.add("user:show");
//stringSet.add("user:admin");
//info.setStringPermissions(stringSet);
return info;
}
/**
* 这里可以注入userService,为了方便演示,我就写死了帐号了密码
* private UserService userService;
* <p>
* 获取即将需要认证的信息
*/
//身份认证。即登录通过账号和密码验证登陆人的身份信息。
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("-------身份认证方法--------");
//加这一步的目的是在Post请求的时候会先进认证,然后在到请求
//先得到用户的身份 看看是否为空
if (authenticationToken.getPrincipal() == null){
return null;
}
//获取用户的名字
String name = authenticationToken.getPrincipal().toString();
//根据用户的名字 调用逻辑层的接口 获取用户的信息
User userByName = demoService.getUserByName(name);
if (userByName == null){
return null;
}
else {
////这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userByName.getUserName(), userByName.getPassword(), getName());
return simpleAuthenticationInfo;
}
//
//String userName = (String) authenticationToken.getPrincipal();
//String userPwd = new String((char[]) authenticationToken.getCredentials());
////根据用户名从数据库获取密码
//String password = "123";
//if (userName == null) {
// throw new AccountException("用户名不正确");
//} else if (!userPwd.equals(password )) {
// throw new AccountException("密码不正确");
//}
//
////交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
//return new SimpleAuthenticationInfo(userName, password,
// ByteSource.Util.bytes(userName + "salt"), getName());
}
}
7 写控制器
@RestController
public class DemoController {
@Resource
private DemoService demoService;
@RequestMapping("/login")
public String login(User user){
//添加用户的认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
user.getUserName(),
user.getPassword()
);
try {
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
// subject.checkRole("admin");
// subject.checkPermissions("query", "add");
} catch (UnknownAccountException uae) {
return "未知账户";
} catch (IncorrectCredentialsException ice) {
return "密码不正确";
} catch (LockedAccountException lae) {
return "账户已锁定";
} catch (ExcessiveAttemptsException eae) {
return "用户名或密码错误次数过多";
} catch (AuthenticationException ae) {
return "用户名或密码不正确!";
} catch (AuthorizationException e) {
e.printStackTrace();
return "没有权限";
}
return "login success";
}
//注解验角色和权限
@RequiresRoles("user")
@RequiresPermissions("query")
@RequestMapping("/index")
public String index() {
return "index!";
}
}
8 注解验证角色和权限的话无法捕捉异常,从而无法正确的返回给前端错误信息,所以我加了一个类用于拦截异常,具体代码如下 MyExceptionHandler.java
package com.wsl.filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
@Slf4j
public class MyExceptionHandler {
@ExceptionHandler
@ResponseBody
public String ErrorHandler(AuthorizationException e) {
log.error("没有通过权限验证!", e);
return "没有通过权限验证!";
}
}
9 开始测试 我用的是postman
使用用户 wsl登陆的时候
当账户/密码错误的时候
使用wsl用户 登陆 index的时候
使用zhangsan用户登陆index的时候
先用zhangsan 帐号完成登陆