1. Shiro简介
1.1 什么是Shiro
- Apache Shiro是一个Java的安全(权限)框架。
- Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。
- Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。
- 下载地址: http://shiro.apache.org/
1.2 有哪些功能?
1.3 Shiro架构 (外部)
从外部来看Shiro,即从应用程序角度来观察如何使用shiro完成工作:
1.4 Shiro架构 (内部)
2. Hello World
2.1 快速实战
查看官网文档: http://shiro.apache.org/tutorial.html
官方的quickstart: https://github.com/apache/shiro/tree/master/samples/quickstart/
在Spring Boot整合shiro中,启动程序出现了下面的错误:
***************************
APPLICATION FAILED TO START
***************************
Description:
Method filterShiroFilterRegistrationBean in org.apache.shiro.spring.config.web.autoconfigure.ShiroWebFilterConfiguration required a bean named 'shiroFilterFactoryBean' that could not be found.
Action:
Consider defining a bean named 'shiroFilterFactoryBean' in your configuration.
原因: 未指定bean的名称,默认采用的是 “方法名” + "首字母小写"的配置方式
解决方法:
- 在生产shiroFilterFactoryBean的方法上指定Bean名称
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean xxx(SecurityManager securityManager) {
...
}
- 在生产shiroFilterFactoryBean的方法上修改方法名字为shiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
...
}
3.简单的shiro-springboot项目
配置文件
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot默认是不注入这些属性值的,需要自己绑定
#druid数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedstatements: true
#配置监控统计拦截的filters,stat: 监控统计,Log4j: 日志记录, wall:防御sql注入
#如果允许时报错java.lang.ClassNotFoundException: org.apache.Log4j.Priority
#则导入Log4j 依赖即可,Maven地址: https://mvnrepository.com/artifact/Log4j/Log4j
filters: stat,wa11,1og4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.s1owSqlMi1lis=500
mybatis.type-aliases-package=com.xz.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
项目结构
核心代码
UserRealm.java
package com.xz.controller;
import com.xz.pojo.User;
import com.xz.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author 许正
* @version 1.0
*/
//自定义的 UserRealm: extends AuthorizingRealm 即可
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了 ==> 授权doGetAuthorizationInfo");
//AuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
//拿到当前登陆的这个对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到User对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了 ==> 认证doGetAuthenticationInfo");
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//连接数据库
User user = userService.queryUserByName(usernamePasswordToken.getUsername());
if (user == null) {
return null;//UnknownAccountException
}
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setAttribute("loginUser", user);
//密码认证, shiro做(加密)
//可以加密: MD5(例: 123456): e10adc3949ba59abbe56e057f20f883e MD5盐值加密: e10adc3949ba59abbe56e057f2ef883e + username
return new SimpleAuthenticationInfo(user, user.getPwd(), "");
}
}
MyController.java
package com.xz.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author 许正
* @version 1.0
*/
@Controller
public class MyController {
@RequestMapping({"/", "/index", "/index.html"})
public String toIndex(Model model) {
model.addAttribute("msg", "Hello, Shiro!");
return "index";
}
@RequestMapping("/user/add")
public String add() {
return "user/add";
}
@RequestMapping("/user/update")
public String update() {
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("/login")
public String login(String username, String password, Model model) {
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登陆数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);//执行登录方法, 如果没有异常就OK
return "index";
} catch (UnknownAccountException e) {//用户名不存在
model.addAttribute("msg", "用户名不存在!");
return "login";
} catch (IncorrectCredentialsException e) {//密码不存在
model.addAttribute("msg", "密码不存在!");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized() {
return "未经授权无法访问此页面";
}
}
ShiroConfig.java
package com.xz.shiroconfig;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.xz.controller.UserRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author 许正
* @version 1.0
*/
@Configuration
public class ShiroConfig {
//3. ShiroFilterFactoryBean
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//关联DefaultWebSecurityManager, 设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon: 无需认证就可以访问
authc: 必须认证了才能让问
user: 必须拥有记住我功能才能用
perms: 拥有对某个资源的权限才能访问;
role: 拥有某个角色权限才能访问
// filterMap.put("/user/add", "anon");
// filterMap.put("/user/update", "authc");
*/
//拦截
Map<String, String> filterMap = new LinkedHashMap<>();
//授权,正常的情况下, 没有授权会跳转到未授权页面
filterMap.put("/user/add", "perms[user:add]");
filterMap.put("/user/update", "perms[user:update]");
filterMap.put("/user/*", "authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录的请求
bean.setLoginUrl("/toLogin");
//未授权页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//2. DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//1. 创建realm对象, 需要自定义类
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
//整合ShiroDialect:用来整合shiro-thymeleaf
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
需要该项目的小伙伴可以私信向我要哦~~
一起加油!!!