1.1、Shiro 是什么?
Apache Shiro 是 Java 的一个安全(权限)框架。
Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。
Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。
下载地址
- 官网:http://shiro.apache.org/
- github:https://github.com/apache/shiro
1.2、有哪些功能?
Authentication:身份认证/登录,验证用户是不是拥有相应的身份
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限
Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储
Web Support:Web 支持,可以非常容易的集成到Web 环境
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率
Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去
Testing:提供测试支持
“Run As”:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了
1.3、Shiro架构(外部)
从外部来看Shiro,即从应用程序角度的来观察如何使用Shiro完成工作
Subject:应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject。Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;与Subject 的所有交互都会委托给SecurityManager;Subject 其实是一个门面,SecurityManager才是实际的执行者
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色
Realm:Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm 看成DataSource
1.4、Shiro架构(内部)
- Subject:任何可以与应用交互的“用户”;
- SecurityManager:相当于SpringMVC中的DispatcherServlet;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证、授权、会话及缓存的管理。
- Authenticator:负责Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
- Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
- Realm:可以有1 个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的Realm;
- SessionManager:管理Session 生命周期的组件;而Shiro并不仅仅可以用在Web 环境,也可以用在如普通的JavaSE环境
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能 - Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密。
2.1、快速实践
查看官方文档:http://shiro.apache.org/tutorial.html
官方的quickstart : https://github.com/apache/shiro/tree/master/samples/quickstart/
创建一个maven父工程,用来学习Shiro,删掉不必要的部分
创建一个普通的Maven子工程:Hello-Shiro
根据官方文档,我们导入Shiro的依赖
版本号点击这里
<dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.6.0</version></dependency><!-- configure logging --><!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j --><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>2.0.0-alpha1</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>2.0.0-alpha1</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies>
相关配置文件
log4j.properties——官网
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n # General Apache libraries log4j.logger.org.apache=WARN # Spring log4j.logger.org.springframework=WARN # Default Shiro logging log4j.logger.org.apache.shiro=INFO # Disable verbose logging log4j.logger.org.apache.shiro.util.ThreadContext=WARN log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
shiro.ini——官网
[users] # user 'root' with password 'secret' and the 'admin' role root = secret, admin # user 'guest' with the password 'guest' and the 'guest' role guest = guest, guest # user 'presidentskroob' with password '12345' ("That's the same combination on # my luggage!!!" ;)), and role 'president' presidentskroob = 12345, president # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' darkhelmet = ludicrousspeed, darklord, schwartz # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' lonestarr = vespa, goodguy, schwartz # ----------------------------------------------------------------------------- # Roles with assigned permissions # # Each line conforms to the format defined in the # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc # ----------------------------------------------------------------------------- [roles] # 'admin' role has all permissions, indicated by the wildcard '*' admin = * # The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:* # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
启动类 Quickstart——官网
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.Session;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * Simple Quickstart application showing how to use Shiro's API. * 简单入门Shiro使用API * * @since 0.9 RC2 */public class Quickstart {private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);public static void main(String[] args) {// The easiest way to create a Shiro SecurityManager with configured// realms, users, roles and permissions is to use the simple INI config.// We'll do that by using a factory that can ingest a .ini file and// return a SecurityManager instance:// Use the shiro.ini file at the root of the classpath// (file: and url: prefixes load from files and urls respectively):// 读取配置文件:Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();// for this simple example quickstart, make the SecurityManager// accessible as a JVM singleton. Most applications wouldn't do this// and instead rely on their container configuration or web.xml for// webapps. That is outside the scope of this simple quickstart, so// we'll just do the bare minimum so you can continue to get a feel// for things.SecurityUtils.setSecurityManager(securityManager);// Now that a simple Shiro environment is set up, let's see what you can do:// get the currently executing user:// 获取当前的用户对象 SubjectSubject currentUser = SecurityUtils.getSubject();// Do some stuff with a Session (no need for a web or EJB container!!!)//通过当前用户拿到Shiro的Session 可以脱离web存值取值Session session = currentUser.getSession();session.setAttribute("someKey", "aValue");String value = (String) session.getAttribute("someKey");if (value.equals("aValue")) {log.info("Retrieved the correct value! [" + value + "]");}// let's login the current user so we can check against roles and permissions://判断当前的用户是否被认证if (!currentUser.isAuthenticated()) {//Token 令牌UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");//设置记住我token.setRememberMe(true);try {//执行登录操作currentUser.login(token);} catch (UnknownAccountException uae) {log.info("There is no user with username of " + token.getPrincipal());} catch (IncorrectCredentialsException ice) {log.info("Password for account " + token.getPrincipal() + " was incorrect!");} catch (LockedAccountException lae) {log.info("The account for username " + token.getPrincipal() + " is locked. " +"Please contact your administrator to unlock it.");}// ... catch more exceptions here (maybe custom ones specific to your application?catch (AuthenticationException ae) {//unexpected condition? error?}}//say who they are://print their identifying principal (in this case, a username):log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");//test a role:// 检查角色if (currentUser.hasRole("schwartz")) {log.info("May the Schwartz be with you!");} else {log.info("Hello, mere mortal.");}//test a typed permission (not instance-level)//粗粒度if (currentUser.isPermitted("lightsaber:wield")) {log.info("You may use a lightsaber ring. Use it wisely.");} else {log.info("Sorry, lightsaber rings are for schwartz masters only.");}//a (very powerful) Instance Level permission://细粒度if (currentUser.isPermitted("winnebago:drive:eagle5")) {log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +"Here are the keys - have fun!");} else {log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");}//all done - log out!//注销currentUser.logout();//结束System.exit(0);}}
Spring Secutrry都有~(只是换了个名字)
// 获取当前的用户对象 SubjectSubject currentUser = SecurityUtils.getSubject();Session session = currentUser.getSession();currentUser.isAuthenticated()currentUser.getPrincipal()currentUser.hasRole("schwartz")currentUser.isPermitted("lightsaber:wield")currentUser.logout();
3.1、SpringBoot整合Shiro环境搭建
新建一个项目或模块,勾选依赖
pom.xml
<dependencies><!--thymeleaf--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><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><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies>
测试环境是否正常
新建一个controller页面
@Controllerpublic class MyController {@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";}}
新建一个index.html页面
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>首页</title></head><body><div><h1>首页</h1><p th:text="${msg}"></p><hr><a th:href="@{/user/add}">add</a> | <a th:href="@{/user/update}">update</a></div></body></html>
新建一个add.html页面
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>add</h1></body></html>
新建一个update.html页面
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>update</h1></body></html>
项目结构
运行截图
导入shiro整合spring的包——官网,查看最新版本
<!-- Subject 用户 SecurityManager 管理所有用户 Realm 连接数据库 --><!--shiro整合spring的包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.6.0</version></dependency>
编写导入配置类
- 创建realm对象,需要自定义类
- DefaultWebSecurityManager
- ShiroFilterFactoryBean
编写一个自定义类UserRealm
//自定义的UserRealmpublic class UserRealm extends AuthorizingRealm {//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");return null;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了=>认证doGetAuthorizationInfo");return null;}}
编写配置ShiroConfig
@Configurationpublic class ShiroConfig {//3. shiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();// 设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);return bean;}//2. DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 关联userRealmsecurityManager.setRealm(userRealm);return securityManager;}//1. 创建realm对象,需要自定义类@Beanpublic UserRealm userRealm() {return new UserRealm();}}
3.2、Shiro实现登录拦截
在ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置
Map<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/user/add","authc");filterMap.put("/user/update","authc");bean.setFilterChainDefinitionMap(filterMap);
- anon: 无需认证就可以访问
- authc: 必须认证了才能访问
- user: 必须拥有记住我功能才能用
- perms: 拥有对某个资源的权限才能访问
- role: 拥有某个角色权限
点击首页的add或者update之后
添加拦截成功页面
登录页面login.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>登录页面</title></head><body><h1>登录</h1><hr><form action=""><p>用户名:<input type="text" name="username"></p><p>密码:<input type="text" name="password"></p><p>密码:<input type="submit"></p></form></body></html>
在MyController中添加
@RequestMapping("/toLogin")public String toLogin() {return "login";}
在ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置
//设置登录的请求bean.setLoginUrl("/toLogin");
拦截成功页面
3.3、Shiro实现用户认证
在MyController中编写用户提交表单之后处理
@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";}}
login.html的修改
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>登录页面</title></head><body><h1>登录</h1><hr><p th:text="${msg}" style="color: red;"></p><form th:action="@{/login}"><p>用户名:<input type="text" name="username"></p><p>密码:<input type="text" name="password"></p><input type="submit"></form></body></html>
用户输入登录信息
页面
控制台
用户认证编写UserRealm中的认证(doGetAuthenticationInfo)
//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行了=>认证doGetAuthorizationInfo");// 用户名、密码, 数据中取String name = "root";String password = "123456";UsernamePasswordToken userToken = (UsernamePasswordToken) token;if (!userToken.getUsername().equals(name)) {return null;//抛出异常 UnknownAccountException}// 密码认证,shiro做return new SimpleAuthenticationInfo("",password,"");}
3.4、Shiro整合Mybatis
导入依赖
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.1</version></dependency><!--引入mybatis,这是MyBatis官方提供的适配spring Boot的,而不是spring Boot自己的--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency>
配置文件application.yml的编写
spring: datasource:username: rootpassword: root#?serverTimezone=UTC解决时区的报错url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource#Spring Boot 默认是不注入这些属性值的,需要自己绑定#druid 数据源专有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4jfilters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20useGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 mybatis: type-aliases-package: com.zhy.pojo mapper-locations: classpath:mapper/*.xml
User类的编写
@Data@AllArgsConstructor@NoArgsConstructorpublic class User {private int id;private String name;private String pwd;}
UserMapper接口编写
package com.zhy.mapper;import com.zhy.pojo.User;import org.apache.ibatis.annotations.Mapper;import org.springframework.stereotype.Repository;@Repository@Mapperpublic interface UserMapper {public User queryUserByName(String name);}
UserMapper.xml映射
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个对应的Dao/Mapper接口--><mapper namespace="nuc.ss.mapper.UserMapper"><select id="queryUserList" resultType="User">select * from mybatis.user;</select><select id="queryUserById" resultType="User">select * from mybatis.user where id = #{id};</select><insert id="addUser" parameterType="User">insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});</insert><update id="updateUser" parameterType="User">update mybatis.user set name=#{name},pwd = #{pwd} where id = #{id};</update><delete id="deleteUser" parameterType="int">delete from mybatis.user where id = #{id}</delete></mapper>
UserService接口实现
public interface UserService {public User queryUserByName(String name);}
UserServiceImpl业务逻辑
@Servicepublic class UserServiceImpl implements UserService {@AutowiredUserMapper userMapper;@Overridepublic User queryUserByName(String name) {return userMapper.queryUserByName(name);}}
测试环境
@SpringBootTestclass ShiroSpringbootApplicationTests {@AutowiredUserServiceImpl userService;@Testvoid contextLoads() {System.out.println(userService.queryUserByName("zhy"));}}
UserRealm连接真实数据库
//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行了=>认证doGetAuthorizationInfo");UsernamePasswordToken userToken = (UsernamePasswordToken) token;// 真实数据库 用户名、密码, 数据中取User user = userService.queryUserByName(userToken.getUsername());if (user == null) {//没有这个人return null;}// 密码认证,shiro做return new SimpleAuthenticationInfo("",user.getPwd(),"");}
断点测试密码加密类型
打断点Debug
默认是SimpleCredentialsMatcher加密
MD5加密——测试
123456——E10ADC3949BA59ABBE56E057F20F883E
MD5盐值加密
所有加密
3.5、Shiro实现用户授权
ShiroConfig中的getShiroFilterFactoryBean方法添加认证代码
//授权,正常情况下,没有授权会跳转到为授权页面filterMap.put("/user/add","perms[user:add]");filterMap.put("/user/update","perms[user:update]");
登录之后点击add按钮会弹出如下页面
添加未授权页面
MyController
@RequestMapping("/noauth")@ResponseBodypublic String unauthorized() {return "未经授权,无法访问此页面";}
ShiroConfig中的getShiroFilterFactoryBean方法中添加
//为授权页面bean.setUnauthorizedUrl("/noauth");
再次测试
所以需要在UserRealm中为用户进行真正授权
给用户表增加权限字段,并修改pojo
设置相应权限
UserRealm类的修改
//自定义的UserRealmpublic class UserRealm extends AuthorizingRealm {@AutowiredUserService userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//拿到当前登录的这个对象Subject subject = SecurityUtils.getSubject();User currentUser = (User)subject.getPrincipal();//拿到user对象//设置当前用户的权限info.addStringPermission(currentUser.getPerms());return info;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {......// 密码认证,shiro做return new SimpleAuthenticationInfo(user,user.getPwd(),"");}}
再次测试
3.6、Shiro整合Thymeleaf
shiro-thymeleaf整合包导入——官网
<!--shiro-thymeleaf整合--><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency>
在ShiroConfig中整合ShiroDialect
// 整合ShiroDialect: 用来整合 Shiro thymeleaf@Beanpublic ShiroDialect getShiroDialect() {return new ShiroDialect();}
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></head><body><div><h1>首页</h1><p th:text="${msg}"></p><!--用session实现,配合UserRealm中的session实现--><!--<div th:if="${session.loginUser==null}"> <a th:href="@{/toLogin}">登录</a> </div>--><div shiro:notAuthenticated><a th:href="@{/toLogin}">登录</a></div><hr><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></div></body></html>
页面显示
3.7、所有代码
ShiroConfig
package com.zhy.config;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;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;@Configurationpublic class ShiroConfig {//3. shiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();// 设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);/* - anon: 无需认证就可以访问 - authc: 必须认证了才能访问 - user: 必须拥有记住我功能才能用 - perms: 拥有对某个资源的权限才能访问 - role: 拥有某个角色权限 */Map<String, String> filterMap = new LinkedHashMap<>();// filterMap.put("/user/add","authc");// filterMap.put("/user/update","authc");bean.setFilterChainDefinitionMap(filterMap);//设置登录的请求bean.setLoginUrl("/toLogin");bean.setUnauthorizedUrl("/noauth");//授权,正常情况下,没有授权会跳转到为授权页面filterMap.put("/user/add","perms[user:add]");filterMap.put("/user/update","perms[user:update]");return bean;}//2. DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 关联userRealmsecurityManager.setRealm(userRealm);return securityManager;}//1. 创建realm对象,需要自定义类@Beanpublic UserRealm userRealm() {return new UserRealm();}// 整合ShiroDialect: 用来整合 Shiro thymeleaf@Beanpublic ShiroDialect getShiroDialect() {return new ShiroDialect();}}
UserRealm
package com.zhy.config;import com.zhy.pojo.User;import com.zhy.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;//自定义的UserRealmpublic class UserRealm extends AuthorizingRealm {@AutowiredUserServiceImpl userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//拿到当前登录的这个对象Subject subject = SecurityUtils.getSubject();User currentUser = (User)subject.getPrincipal();//拿到user对象//设置当前用户的权限info.addStringPermission(currentUser.getPerms());return info;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了=>认证doGetAuthorizationInfo");UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;// 真实数据库 用户名、密码, 数据中取User user = userService.queryUserByName(userToken.getUsername());if (user == null) {//没有这个人return null;}// 密码认证,shiro做return new SimpleAuthenticationInfo(user,user.getPwd(),"");}}
MyController
package com.zhy.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;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class MyController {@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);//执行登录的方法,如果没有异常就说明ok了return "index";} catch (UnknownAccountException e) {//用户名不存在model.addAttribute("msg","用户名错误");return "login";} catch (IncorrectCredentialsException e) {//密码不存在model.addAttribute("msg","密码错误");return "login";}}@RequestMapping("/noauth")@ResponseBodypublic String unauthorized() {return "未经授权,无法访问此页面";}}
pom依赖
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.zhy</groupId><artifactId>springboot_07_shiro</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot_07_shiro</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.1</version></dependency><!--shiro-thymeleaf整合--><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency><!--引入mybatis,这是MyBatis官方提供的适配spring Boot的,而不是spring Boot自己的--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--shiro整合spring的包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.6.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
让 Apache Shiro 保护你的应用