Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account) 或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
-
1.导入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- druid数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.6</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!--shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.7.1</version> </dependency> <!-- thymeleaf-extras-shiro --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> </dependencies>
-
2.application.yaml配置文件
spring.datasource.username: root spring.datasource.password: 123456 spring.datasource.url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver spring.datasource.type: com.alibaba.druid.pool.DruidDataSource #druid数据源配置 spring.datasource.filters: stat,wall,log4j spring.datasource.maxPoolPreparedStatementPerConnectionSize: 20 spring.datasource.useGlobalDataSourceStat: true spring.datasource.connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMills=500 # mybatis配置 mybatis.type-aliases-package: com.lv.pojo mybatis.mapper-locations: classpath:mybatis/mapper/*.xml spring.mvc.format.Date: yyyy-MM-dd
-
3.pojo+mapper+service
pojo:
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Integer id; private String name; private String pwd; private String perms; //权限 }
mapper:
@Repository @Mapper public interface UserMapper { public User queryByName(@Param("name") String name); }
UserMapper.xml(resource/mybatis/mapper)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lv.mapper.UserMapper"> <!--按名字查询--> <select id="queryByName" resultType="User"> select * from user where name=#{name}; </select> </mapper>
service:
public interface UserService { public User queryByName(String name); }
@Service("userServiceImpl") public class UserServiceImpl implements UserService{ @Autowired private UserMapper userMapper; @Override public User queryByName(String name) { return userMapper.queryByName(name); } }
-
4.shiro配置类
UserRealm.java:
//自定义UserRealm,需继承AuthorizingRealm public class UserRealm extends AuthorizingRealm { @Autowired @Qualifier("userServiceImpl") private UserServiceImpl userService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行了=>授权doGetAuthorizationInfo"); //授予用户权限 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //info.addStringPermission("user:add"); //拿到当前登陆的对象 Subject subject = SecurityUtils.getSubject(); User currentUser = (User) subject.getPrincipal(); info.addStringPermission(currentUser.getPerms()); return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("执行了=>认证doGetAuthorizationInfo"); //连接数据库 UsernamePasswordToken userToken = (UsernamePasswordToken) token; //用户名验证 User user = userService.queryByName(userToken.getUsername()); System.out.println("user===>"+user); if (user==null){ //没有此人 return null; //抛出UnknownAccountException异常 } Subject currentSubject = SecurityUtils.getSubject(); Session session = currentSubject.getSession(); session.setAttribute("loginUser",user); //密码认证,有shiro完成 return new SimpleAuthenticationInfo(user,user.getPwd(),""); } }
ShiroConfig.java
@Configuration public class ShiroConfig { //3.ShiroFilterFactoryBean,类名不能乱写,否则可能会报错 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("MySecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); //添加shiro内置过滤器 /* anon:无需认证就可访问 authc:必须认证后才可访问 user:必须拥有记住我功能才能用 perms:拥有对某个资源的权限才能访问 role:拥有某个角色的权限才能访问 * */ Map<String,String> filterMap=new LinkedHashMap<>(); filterMap.put("/user/add","perms[user:add]"); //设置访问权限user:add filterMap.put("/user/update","perms[user:update]"); //设置访问权限user:update filterMap.put("/user/*","authc"); //访问需要认证 filterMap.put("/logOut","logout"); //退出系统 bean.setFilterChainDefinitionMap(filterMap); //设置登录请求 bean.setLoginUrl("/toLogin"); //未授权执行此请求 bean.setUnauthorizedUrl("/unAuthorize"); return bean; } //2.DefaultWebSecurityManager @Bean(name = "MySecurityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联UserRealm securityManager.setRealm(userRealm); return securityManager; } //1.创建realm对象,需要自定义类 @Bean public UserRealm userRealm(){ UserRealm userRealm = new UserRealm(); //是否开启md5加密 //userRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return userRealm; } //md5加密 @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); //利用md5进行加密 hashedCredentialsMatcher.setHashAlgorithmName("md5"); //加密次数 hashedCredentialsMatcher.setHashIterations(1024); return hashedCredentialsMatcher; } //整合shiro-thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } }
-
5.控制器
@Controller public class MyController { @Autowired private UserRealm userRealm; @RequestMapping({"/","/index"}) 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"; //出现异常,返回登录页 } } @GetMapping("/unAuthorize") @ResponseBody public String UnAuthorize(){ return "没有访问权限"; }
-
6.前端页面
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"> <head> <meta charset="UTF-8"> <title>$Title$</title> </head> <body> <h1>首页</h1> <p th:text="${msg}"></p> <div th:if="${session.loginUser==null}"> <a th:href="@{/toLogin}">前往登录</a> </div> <div th:if="${session.loginUser!=null}"> <a th:href="@{/logOut}">退出</a> </div> <div shiro:hasPermission="user:add"> <a th:href="@{/user/add}">add</a> </div> <div shiro:hasPermission="user:update"> | <a th:href="@{/user/update}">update</a> </div> </body> </html>
login.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>$Title$</title> </head> <body> <h1>登录</h1> <form th:action="@{/login}" method="post"> <p style="color: red" th:text="${msg}"></p> <input type="text" th:name="username"><br> <input type="text" th:name="password"><br> <input type="submit" value="登录"> </form> </body> </html>
(1)未认证时的首页
(2)认证后的首页,实现不同权限展示不同页面