Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。



org.springframework.boot spring-boot-starter-parent2.5.3
org.springframework.boot spring-boot-starter-web
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Description: SpringBoot启动类
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Description:控制层
*/
@Controller
public class TestController {
/**
* 测试方法
*/
@GetMapping("/hello")
@ResponseBody
public String hello(){
System.out.println("UserController.hello()");
return "ok";
}
}

org.springframework.boot spring-boot-starter-thymeleaf
/**
* 测试thymeleaf
*/
@RequestMapping("/test")
public String testThymeleaf(Model model){
//把数据存入model
model.addAttribute("name", "张三");
//返回test.html
return "test";
}
Title

org.apache.shiro shiro-spring1.10.1
package com.example.shiro;
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;
/**
* @Description:自定义Realm 处理登录 权限
*/
public class UserRleam 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;
}
}
package com.example.shiro;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Description:shiro配置类
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
Map filterMap = new LinkedHashMap();
filterMap.put("/hello", "anon");
filterMap.put("/login", "anon");
filterMap.put("/**", "authc");
//要求登陆时的链接,非必须。
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean
public UserRealm getRealm(){
return new UserRealm();
}
}
登陆页面
登录
/**
* 登陆页面跳转
*/
@GetMapping("/login")
public String login(){
return "login";
}
/**
* 登录逻辑处理
*/
@PostMapping("/login")
public String login(String username,String password,Model model){
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
//跳转到test.html
return "redirect:/test";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登录失败:用户名不存在
System.out.println("用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败:密码错误
System.out.println("密码错误");
return "login";
}
}
package com.example.shiro;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @Description:自定义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 username = "aaa";
String password = "123";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
if(!token.getUsername().equals(username)){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
/**
* 对比密码
* 参数1:主体对象,按需要传,登陆成功后该参数可通过SecurityUtils.getSubject().getPrincipal()获取。
* 参数2:从对象中取密码,users.getPassword()是这个用户的数据库中的密码 是用来和authenticationToken里的密码比对
* 参数3:盐,可以为空
* 参数4:当前realm的名字
*/
return new SimpleAuthenticationInfo("flk好帅", password,null, getName());
}
}

由于在shiro过滤器中添加了filterMap.put(“/hello”, “anon”);,所以无需认证(登录)就可以访问/hello

由于在shiro过滤器中添加了filterMap.put(“/**”, “authc”);,所以必须认证才可以访问/test,页面便跳转到登陆页面/login

可以正常进入到test.html。

控制台

账号或密码不正常,根据controller层的判断逻辑会跳转到登陆页面/login
com.alibaba
druid-spring-boot-starter
1.2.15
mysql
mysql-connector-java
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
在src/main/resources目录下创建application.yml文件,并添加配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: admin
type: com.alibaba.druid.pool.DruidDataSource
# MyBatis
mybatis:
# 搜索指定包别名
type-aliases-package: com.example.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 日志配置
logging:
level:
com.example: debug

package com.example.domain;
/**
* @Description:User类
*/
public class User {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.example.mapper;
import com.example.domain.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
/**
* @Description:Usermapper
*/
public interface UserMapper {
User findByUsername(@Param("username") String username);
}
在src/main/resources/mapper目录下
package com.example.service;
import com.example.domain.User;
/**
* @Description:IUserService
*/
public interface IUserService {
/**
* 根据用户名查询用户
* @param username 用户名
* @return
*/
User findByUsername (String username);
}
package com.example.service.impl;
import com.example.domain.User;
import com.example.mapper.UserMapper;
import com.example.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Description:UserServiceImpl
*/
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public User findByUsername(String username) {
return userMapper.findByUsername(username);
}
}
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Description: SpringBoot启动类
*/
@SpringBootApplication
//@MapperScan指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
@MapperScan("com.example.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.example.shiro;
import com.example.domain.User;
import com.example.service.IUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @Description:自定义Realm 处理登录 权限
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
private IUserService userService;
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库的用户名和密码
// String username = "aaa";
// String password = "123";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//从数据库中获取用户信息
User user = userService.findByUsername(token.getUsername());
if(user == null){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
/**
* 对比密码
* 参数1:主体对象,按需要传,登陆成功后该参数可通过SecurityUtils.getSubject().getPrincipal()获取。
* 参数2:从对象中取密码,users.getPassword()是这个用户的数据库中的密码 是用来和authenticationToken里的密码比对
* 参数3:盐,可以为空
* 参数4:当前realm的名字
*/
return new SimpleAuthenticationInfo("flk好帅", user.getPassword(),null, getName());
}
}


org.springframework.boot spring-boot-starter-aop
/**
* 开启Shiro注解
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
DefaultWebSecurityManager defaultWebSecurityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//假设数据库的aaa用户的权限只有user:add
info.addStringPermission("user:add");
//info.addStringPermission("user:update");
return info;
}
add
addPage
update
updatePage
Title
进入添加页面: 添加
进入更新功能: 更新
/**
* 添加页面跳转
*/
//需要user:add权限才能访问
@RequiresPermissions("user:add")
@GetMapping("/add")
public String add(){
return "add";
}
/**
* 添加页面跳转
*/
//需要user:update权限才能访问
@RequiresPermissions("user:update")
@GetMapping("/update")
public String update(){
return "update";
}

成功进入,因为它有user:add这个权限。
– 控制台打印:


失败进入,因为它没有user:update这个权限。
– 控制台打印:

(后续可以做全局异常捕获跳转到提示页面,我这边没有去弄。。。)
添加thymeleaf与shiro的扩展
com.github.theborakompanioni thymeleaf-extras-shiro2.0.0
配置ShiroDialect,用于thymeleaf和shiro标签配合使用
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
Title
进入添加页面: 添加
进入更新功能: 更新

控制台打印: