- Apache Shiro 是一个Java的安全(权限)框架 ------- 和SpringSecurity一样是安全框架
- Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSe环境.
- Shiro 可以完成: 认证,授权,加密,会话管理,Web集成,缓存等
- 下载地址:https://shiro.apache.org/ 官网
- 导入依赖
org.apache.shiro
shiro-spring
1.4.1
- springboot整合的shiro启动器并不好用,甚至会出现拦截器拦截不到的情况,所以还是用shiro-spring这个依赖
- 写html页面
add.html
Document
增加页面
update.html
Document
更新页面
index.html
Document
首页
add | update
login.html
Title
登录
- controller层映射
package com.example.controller;
import org.apache.shiro.SecurityUtils;
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;
@Controller
public class MyController {
@RequestMapping("/")
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);//执行登录的方法,如果没有异常就可以了
return "index";
} catch (UnknownAccountException e) {//用户名不存在
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException e) {//密码不存在
model.addAttribute("msg", "密码错误");
return "login";
}
}
}
3、Shiro实现功能
- shiro配置
ShiroConfig.java
package com.example.config;
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.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean 第三步 关联第二步
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean =new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
Map filterMap = new HashMap<>();
filterMap.put("/user/add","anon");
filterMap.put("/user/update","authc");
//设置访问不了时跳转到登录页面
bean.setLoginUrl("/toLogin");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
//DefaultWebSecurityManager 第二步 关联第一步
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
// 创建realm对象,需要自定义类 第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
- 创建userRealm对象
UserRealm.java
package com.example.config;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthenticationInfo");
//用户名和密码认证 数据库中取
String name = "root";
String password="123456";
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
if (!userToken.getUsername().equals(name)){
return null; //抛出异常,异常就是前面自定义的 UnknownAccountException e
}
//密码认证不需要我们做,shiro会自动做
return new SimpleAuthenticationInfo("",password,"");
}
//授权
}
- 登录拦截
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean =new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
Map filterMap = new HashMap<>();
filterMap.put("/user/add","anon");
filterMap.put("/user/update","authc");
//设置访问不了时跳转到登录页面
bean.setLoginUrl("/toLogin");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
记得要用bean注入,不然拦截不了
- 实现用户认证
MyControlller.java
@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);//执行登录的方法,如果没有异常就可以了
return "index";
} catch (UnknownAccountException e) {//用户名不存在
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException e) {//密码不存在
model.addAttribute("msg", "密码错误");
return "login";
}
}
UserRealm.java
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthenticationInfo");
//用户名和密码认证 数据库中取
String name = "root";
String password="123456";
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
if (!userToken.getUsername().equals(name)){
return null; //抛出异常,异常就是前面自定义的 UnknownAccountException e
}
//密码认证不需要我们做,shiro会自动做
return new SimpleAuthenticationInfo("",password,"");
}
//授权
- 退出注销功能
- 在index页面设计退出按钮
注销
- myController中去映射
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "index";
}
4、Shiro整合Mybatis
-
导入mysql依赖
org.projectlombok lombok mysql mysql-connector-java log4j log4j 1.2.12 com.alibaba druid 1.2.3 org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.2 -
配置数据源以及绑定mybatis
application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
username: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
type: com.alibaba.druid.pool.DruidDataSource
#SpringBoot默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters, stat:监控统计 , log4j:日志记录 , wall:防御sql注入
#如果运行时报错,导入log4j即可(maven)
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#整合mybatis
mybatis:
type-aliases-package: com.example.pojo
mapper-locations: classpath:mapper
//拦截
Map filterMap = new HashMap<>();
//设权限,登录后要进入add必须有进入add页面的权限user:add
filterMap.put("/user/add","perms[user:add]");//给add页面设置权限
filterMap.put("/user/update","perms[user:update]");//给update页面设置权限
//设权限,要进入user/下的页面需要登录
filterMap.put("/user/*","authc");
//设置访问不了时跳转到登录页面
bean.setLoginUrl("/toLogin");
//设置未授权页面
bean.setUnauthorizedUrl("/noauth");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
//DefaultWebSecurityManager 第二步 关联第一步
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
// 创建realm对象,需要自定义类 第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
- UserRealm.java
package com.example.config;
import com.example.pojo.User;
import com.example.service.UserServiceImpl;
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.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServiceImpl userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的对象
Subject subject = SecurityUtils.getSubject();
User currentUser =(User) subject.getPrincipal(); //拿到User对象
//设置当前用户的权限
if (currentUser.getPerms()!=null) {
info.addStringPermission(currentUser.getPerms());//如果当前用户有权限,就添加授权
return info;
}
else {
return null; //如果没有有返回null
}
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthenticationInfo");
// //用户名和密码认证 数据库中取
// String name = "root";
// String password="123456";
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
//连接真实数据库来验证
User user = userService.queryUserByName(userToken.getUsername());
if (user==null){//说明没有这个人
return null; //走到前面的异常UnknownAccountException
}
//数据库中这个人就继续进行密码验证,加密了
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
//授权
}
6、Shiro整合thymeleaf
- 导入依赖
com.github.theborakompanioni
thymeleaf-extras-shiro
2.1.0
- 在前端实现,登录账号后,有这个权限的人才能看到进入这个页面的入口
- 设计一个登录的按钮,并且登录过后便不需要再显示这个登录入口(即判断有无session)
- 获取session从登录认证的地方
UserRealm.java
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
- 前端实现
- 运行测试



