环境
- springboot
- shiro
简介
我这里只讲一个shiro简单的demo,原理大家可以深入学习其他的文章
实现过程
1、首先需要导入pom依赖
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<!--thymeleaf依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--shiro整合thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2、写配置类
@Configuration
public class ShiroConfig {
//3. shiroFilterfactaryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);//设置安全管理器
shiroFilterFactoryBean.setLoginUrl("/login");//没有认证后跳到的页面
/**
* shiro的内置过滤器
anon:无需认证就可以访问 默认
authc:必须认证了才能访问
user:必须拥有记住我功能才能访问
perms:必须拥有对某个的权限才能访问
role:拥有某个角色权限才能访问
*/
//添加shiro的内置过滤器 设置要拦截的url
Map<String,String> filterChainDefinitionMap=new LinkedHashMap<>();//拦截
/index路径请求必须认证才能访问 filterChainDefinitionMap.put("/index","authc");//
//不需要认证
filterChainDefinitionMap.put("/login.html","anon");
filterChainDefinitionMap.put("/hello","authc");
//设置注销的url
filterChainDefinitionMap.put("/logout","logout");
filterChainDefinitionMap.put("/add.html","authc");
filterChainDefinitionMap.put("/404.html","authc");
//filterChainDefinitionMap.put("/del","authc");//del必须认证才能访问
// filterChainDefinitionMap.put("user/**","authc");//支持通配符
//授权
//用户为guest才有权限访问add.html页面
filterChainDefinitionMap.put("/add.html","roles[guest]");
//拥有user:add权限才可以访问 filterChainDefinitionMap.put("/404.html","perms[user:add]");
//没有这个user:delete权限的会被拦截下来 //filterChainDefinitionMap.put("/del","perms[user:delete]");
//未授权的跳转的url
shiroFilterFactoryBean.setUnauthorizedUrl("/login");
/*设置登录成功之后跳转的页面,这个页面是一个附加请求,也就是说如果登录成功之后没有设置跳转页面他才会跳转到这个页面*/
//shiroFilterFactoryBean.setSuccessUrl("/success.html");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);//把设置好的过滤设置到ShiroFilterFactoryBean
return shiroFilterFactoryBean;
}
//2. DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm对象 userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//1. 创建realm对象 自定义的·类
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合shiroDialect:用来整合shiro-thymeleaf
@Bean
public ShiroDialect getshiroDialect(){
return new ShiroDialect();
}
/**
* 下面两个配置是为了让shiro的注解有效,
* 如果不满足注解会自动抛异常,所以这个时候就需要写全局异常处理
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
return authorizationAttributeSourceAdvisor;
}
}
3、认证与授权
public class UserRealm extends AuthorizingRealm {
@Autowired(required = false)
private UserMapper userMapper;
@Autowired
private UserService userService;
/**
* 授权:授权的主要目的就是根据认证数据提取到用户的权限信息
* principalCollection:包含了所有已经认证的安全数据
* AuthorizationInfo:授权数据
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取身份信息
String name= (String) principalCollection.getPrimaryPrincipal();
//根据身份信息从数据库中查询角色和权限数据
User user=userService.queryByName(name);
//这里使用静态数据模拟
Set<String> permissions=new HashSet<>();
permissions.add(user.getPermission());
Set<String> roles=new HashSet<>();
roles.add(user.getRole());
//构造返回
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//设置权限集合
info.setStringPermissions(permissions);
//设置角色集合
info.setRoles(roles);
return info;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//加这一步的目的是在Post请求的时候会先进认证,然后在到请求
if (authenticationToken.getPrincipal() == null) {
return null;
}
//获取用户信息
String name = authenticationToken.getPrincipal().toString();
User user = userService.queryByName(name);
if (user == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
return simpleAuthenticationInfo;
}
}
}
4、实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@TableField("id")
private Long Id;
private String name;
private Integer age;
private String email;
private String password;
private String role;
private String permission;
}
5、mapper类
/**
* 直接继承 BaseMapper,这是 mybatis-plus 封装好的类。
*/
public interface UserMapper extends BaseMapper<User> {
User queryByName(String username);
}
6、业务类
public interface UserService {
User queryByName(String username);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired(required = false)
private UserMapper userMapper;
@Override
public User queryByName(String username) {
QueryWrapper wrapper=new QueryWrapper();
wrapper.eq("name",username);
User user=userMapper.selectOne(wrapper);
return user;
}
}
7、控制类
@Controller
public class UserController {
@Autowired(required = false)
RoleService roleService;
@RequestMapping("/tologin")
public String tologin(User user){
//获取主体
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(user.getName(), user.getPassword()));
System.out.println(subject.hasRole("role1"));
System.out.println(subject.isPermitted("user:save"));
} catch (UnknownAccountException e) {
System.out.println("账号不存在");
return "login";
} catch (AccountException e) {
System.out.println("账号或密码不正确");
return "login";
} catch (IncorrectCredentialsException e) {
System.out.println("账号或密码不正确");
return "login";
}
return "index";
}
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/index")
public String index(){
return "index";
}
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello";
}
/**
* 注销成功之后会默认跳转的index.html页面,所以我这样写是无效的
* 可以自己去修改跳转的页面
* @return
*/
@RequestMapping("/logout")
public String logout(){
return "login";
}
@RequestMapping("/role")
@RequiresAuthentication
@ResponseBody
public String testRole(){
return "role";
}
}
8、页面
404.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
404
</body>
</html>
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加</title>
</head>
<body>
添加
</body>
</html>
delete.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>删除</title>
</head>
<body>
删除
</body>
</html>
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
首页
</body>
</html>
login.html:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form th:action="/tologin">
<input type="text" name="name" value="">
<input type="password" name="password" value="">
<input type="submit" value="登录">
</form>
</body>
</html>
需要认证的页面都会先跳转到登录页面,登录成功之后才可以访问其他页面