Spring Boot 与Shrio整合
2019-06-10 本文已影响0人
過眼云烟
分析Shrio的核心API
Subject:用户主体(把操作交给SecurityManager)
SecurityManager:安全管理器(关联Realm)
Realm:Shiro连接数据的桥梁
Spring Boot整合Shiro
(1) 导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0></version>
<dependency/>
(2)自定义Realm类
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* 自定义Realm
*/
public class UserRealm extends AuthorizingRealm {
/**
* 执行授权逻辑
* @param principalCollection
* @return
*/
//@Qualifier("userRealm")将userRealm注入当前类
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
return null;
}
}
(3)编写Shiro配置类(*)
建立shiro的配置类
/**
* shiro配置类
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
//@Qualifier("userRealm")将userRealm注入当前类
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean
public UserRealm getRealm(){
return new UserRealm();
}
(4) 使用Shiro内置过滤器实现页面拦截,在ShiroConfig中配置
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shrio内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon:无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map<String ,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/add","authc");
// filterMap.put("/update","authc");
filterMap.put("/login","anon");
filterMap.put("/tologin","anon");
filterMap.put("/getinfo","anon");
filterMap.put("/*","authc");
shiroFilterFactoryBean.setLoginUrl("/tologin"); //自定义设置拦截跳转登录页面,默认是login.jsp
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
UserController中设置登录
@PostMapping (value = "/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:getinfo";
} catch (UnknownAccountException e) { //UnknownAccountException shiro用户名不存在抛异常
// e.printStackTrace();
//登录失败
model.addAttribute("msg","用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败
model.addAttribute("msg","密码错误");
return "login";
}
在UserRealm中,执行认证逻辑中,编写登录逻辑
/**
* 执行认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库的用户名和密码 模拟数据库
String name = "liwei";
String password = "123456";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
if(!token.getUsername().equals(name)){
//用户名不存在
return null; //shiro底层会抛出UnKnowAccountExceeption
}
//2 判断密码
return new SimpleAuthenticationInfo("",password,"");
}
整合mybatis
添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
编写application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.108.128:3306/tensquare_user
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource #druid数据源
mybatis:
type-aliases-package: com.liwei.domain #配置实体路径在mapper.xml中,返回实体可以不用写前置路径
mapper-locations: mapperXml/*.xml
在启动类上加扫mapper(dao)注解
@SpringBootApplication
@MapperScan("com.liwei.shiro.mapper")
public class ShiroApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroApplication.class);
}
}
mapper
public interface UserMapper {
User findByMobil(String mobile);
}
service
public interface UserService {
User findByMobil(String mobile);
}
serviceimpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findByMobil(String mobile) {
return userMapper.findByMobil(mobile);
}
}
controller
@PostMapping (value = "/login")
public String login(String mobile,String password,Model model){
/**
* 使用Shiro编写认证操作
*/
//1.获取SUbject
Subject subject = SecurityUtils.getSubject();
//2分装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(mobile,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
return "redirect:getinfo";
} catch (UnknownAccountException e) {
// e.printStackTrace();
//登录失败
model.addAttribute("msg","用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败
model.addAttribute("msg","密码错误");
return "login";
}
}
}
在自定义Realm中编写登录验证
/**
* 自定义Realm
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 执行授权逻辑
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库的用户名和密码 模拟数据库
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.findByMobil(token.getUsername());
if(user==null){
//用户名不存在
return null; //shiro底层会抛出UnKnowAccountExceeption
}
//2 判断密码
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
}
使用shiro内置过滤器拦截资源
/**
* shiro配置类
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shrio内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon:无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map<String ,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/add","authc");
// filterMap.put("/update","authc");
filterMap.put("/login","anon");
filterMap.put("/tologin","anon");
filterMap.put("/getinfo","anon");
filterMap.put("/*","authc");
TODO授权过滤器
// 注意:当前授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/user/add","perms[user:add]"); // user/add 拦截的路径名
//设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth"); //自定义的提示页面路径 /noauth 路径请求地址
shiroFilterFactoryBean.setLoginUrl("/tologin"); //自定义设置拦截跳转登录页面,默认是login.jsp
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
//@Qualifier("userRealm")将userRealm注入当前类
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name = "userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
实现数据库动态绑定
在数据库中添加一个权限字段
perms 数值设置为:user:add user:update user:delete
#### 增加依赖 实现thymeleaf动态标签
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
**
* 自定义Realm
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 执行授权逻辑
* @param principalCollection
* @return
*/
@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.getPerms()); //设置授权字段号
return info;
}
/**
* 执行认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库的用户名和密码 模拟数据库
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.findByMobil(token.getUsername());
if(user==null){
//用户名不存在
return null; //shiro底层会抛出UnKnowAccountExceeption
}
//2 判断密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
配置拦截器信息
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shrio内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon:无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map<String ,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/add","authc");
// filterMap.put("/update","authc");
filterMap.put("/login","anon");
filterMap.put("/tologin","anon");
filterMap.put("/getinfo","anon");
filterMap.put("/*","authc");
//对用户的修改功能进行授权
filterMap.put("/user/update","perms[user:update]");
//授权过滤器
// 注意:当前授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/user/add","perms[user:add]");
//设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
shiroFilterFactoryBean.setLoginUrl("/tologin"); //自定义设置拦截跳转登录页面,默认是login.jsp
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
//@Qualifier("userRealm")将userRealm注入当前类
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name = "userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
/**
* 配置shiroDialect,用于thymeleaf和shiro标签配合使用
*/
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
controller
@Autowired
private UserService userService;
@GetMapping("/noauth")
public String noauth(){
return "/noauth";
}
@PostMapping (value = "/login")
public String login(String mobile,String password,Model model){
/**
* 使用Shiro编写认证操作
*/
//1.获取SUbject
Subject subject = SecurityUtils.getSubject();
//2分装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(mobile,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
return "redirect:getinfo";
} catch (UnknownAccountException e) {
// e.printStackTrace();
//登录失败
model.addAttribute("msg","用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败
model.addAttribute("msg","密码错误");
return "login";
}
}
}
前端页面--结合动态标签实现隐藏连接,有增加的用户看不到修改的
<!DOCTYPE html>
<html xmlns:shiro="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8"/>
<title>测试页面</title>
</head>
<body>
用户测试页面
<div>
<h3 th:text="${name}"></h3>
</div>
<hr/>
<div shiro:hasPermission="user:add">
进入用户添加功能: <a href="user/add">添加用户</a><br/>
</div>
<div shiro:hasPermission="user:update">
进入用户更新功能: <a href="user/update">更新用户</a><br/>
</div>
</body>
</html>