Shiro实现用户认证
SpringBoot与Shiro整合实现用户认证
Shiro的核心API
- Subject:用户主体(把操作交给Security Manager)
- SecurityManager:安全管理器(关联Realm)
- Realm:Shiro连接数据的桥梁
整合
修改pom.xml
<!-- shiro与springboot整合依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
自定义Realm类
package com.example.demo_shiro.shiro;
/**
* 自定义Realm
*/
public class UserRealm extends AuthorizingRealm {
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
return null;
}
}
编写Shiro配置类
package com.example.demo_shiro.shiro;
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon:无需认证(登陆)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能,可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/add", "anon");
filterMap.put("/update", "authc");
filterMap.put("/login","anon");
// filterMap.put("/*", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 修改登陆的页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 关联userRealm
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
/**
* 创建Realm
*/
@Bean("userRealm")
public UserRealm getRealm() {
return new UserRealm();
}
}
使用Shiro内置过滤器实现页面拦截
- Shiro内置过滤器,可以实现权限相关的拦截器
的过滤器:- anon:无需认证(登陆)可以访问
- authc:必须认证才可以访问
- user:如果使用rememberMe的功能,可以直接访问
- perms:该资源必须得到资源权限才可以访问
- role:该资源必须得到角色权限才可以访问
设计登陆页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>登陆页面</title>
</head>
<body>
登陆
<h3 th:text="${msg}" style="color: red;"></h3>
<form method="post" action="login">
用户名 <input type="text" name="name">
密码 <input type="password" name="password">
<button type="submit">登陆</button>
</form>
</body>
</html>
实现用户认证(登陆)操作
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("/login")
public String login(String name, String password, Model model) {
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
//3.执行登陆方法
try {
subject.login(token);
//登陆成功
return "redirect:/hello";
} catch (UnknownAccountException e) {
// 登陆失败:用户名不存在
e.printStackTrace();
model.addAttribute("msg", "用户名不存在");
//重定向消息就带不过去,所以这里就直接跳转了
return "login";
} catch (IncorrectCredentialsException e) {
// 登陆失败:密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
编写Realm的判断逻辑
package com.example.demo_shiro.shiro;
/**
* 自定义Realm
*/
public class UserRealm extends AuthorizingRealm {
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库中的数据如下
String name = "name";
String password = "123";
// 编写shiro判断逻辑,判断用户名和密码
// 1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
if (!name.equals(token.getUsername())) {
//用户名不存在,shiro底层会抛出UnknownAccountException
return null;
}
// 2.判断密码
return new SimpleAuthenticationInfo("",password,"");
}
}
整合Mybatis
配置数据源
spring.datasource.dirverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.type-aliases-package=com.example.domain
编写Mapper接口
com.example.mapper
SpringBoot与Shiro整合实现用户授权
使用Shiro内置过滤器拦截资源
ShiroConfig.java
// 授权过滤器
//注意:当授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/add", "perms[user:add]");
// 设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
完成Shiro授权
UserRealm.java
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
// 给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加资源的授权字符串
info.addStringPermission("user:add");
return null;
}
附录
ShiroConfig与UserRealm代码
ShiroConfig
package com.example.demo_shiro.shiro;
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon:无需认证(登陆)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能,可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/add", "anon");
filterMap.put("/update", "authc");
filterMap.put("/login","anon");
// 授权过滤器
//注意:当授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/add", "perms[user:add]");
filterMap.put("/update", "perms[user:update]");
// 设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 修改登陆的页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 关联userRealm
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
/**
* 创建Realm
*/
@Bean("userRealm")
public UserRealm getRealm() {
return new UserRealm();
}
}
UserRealm.java
package com.example.demo_shiro.shiro;
/**
* 自定义Realm
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
// 给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加资源的授权字符串
//info.addStringPermission("user:add");
// 到数据库查询当前登陆用户的授权字符串
// 获取当前用户
Subject subject = SecurityUtils.getSubject();
User user = (User)subject.getPrincipal();
User dbUser = userService.findById(user.getId());
info.addStringPermission(dbUser.getAuthCodeString());
return info;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
// 编写shiro判断逻辑,判断用户名和密码
// 1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.findByName(token.getUsername());
//假设数据库中的数据如下
if (!("jack".equals(token.getUsername())||"rose".equals(token.getUsername())||"person".equals(token.getUsername()))) {
//用户名不存在,shiro底层会抛出UnknownAccountException
return null;
}
// 2.判断密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}