SpringBoot-09 Shiro
Hello Shiro
可以先创建一个最单纯的Maven项目。
1.导入依赖
<dependencies>56c<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.7.0</version></dependency><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>
这里我导入的依赖基本上都是最新版,如果想要别的版本可以自行搜索导入
2.创建log4j.properties
log4j.rootLogger=INFO, stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n# General Apache librarieslog4j.logger.org.apache=WARN# Springlog4j.logger.org.56cspringframework=WARN# Default Shiro logginglog4j.logger.org.apache.shiro=INFO# Disable verbose logginglog4j.logger.org.apache.shiro.util.ThreadContext=WARNlog4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
3.创建shiro.ini
这里第一次创建ini文件,会让你选择,可以选择txt文件,创建成功后会提示你创下载一个ini插件:
[users]root = secret, adminguest = guest, guestpresidentskroob = 12345, presidentdarkhelmet = ludicrousspeed, darklord, schwartzlonestarr = vespa, goodguy, schwartz[roles]admin = *schwartz = lightsaber:*goodguy = winnebago:drive:eagle5
4.创建Quickstart
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;public class Quickstart {private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);public static void main(String[] args) {// The easiest way to c56create 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:Subject currentUser = SecurityUtils.getSubject();// Do some stuff with a Session (no need for a web or EJB container!!!)Session session = currentUser.getSession();session.setAttribute(\"someKey\", \"aValue\")ad0;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()) {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);}}
5.启动测试
6.可能遇到错误
解决办法:这是因为你的依赖中的问题:
<dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>2.0.0-alpha1</version><scope>test</scope></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>2.0.0-alpha1</version><scope>test</scope></dependency>
依赖中存在 test,会出现错误,删掉就行了。
Shiro环境搭建
对于shiro环境,一共有三个要素:
- ShiroFilterFactoryBean
- DefaultWebSecurityManager
- Realm
我们需要倒着来创建
创建一个config文件夹,和ShiroConfig、UserRealm类
public class UserRealm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {return null;}}
@Configurationpublic class ShiroConfig {//ShiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier(\"defaultWebSecurityManager\") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);return bean;}//DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier(\"userRealm\") UserRealm userRealm){DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager();SecurityManager.setRealm(userRealm);return SecurityManager;}//创建 realm 对象@Beanpublic UserRealm userRealm(){return new UserRealm();}}
以上就完成了shiro的基本框架搭建。
登录拦截
1.前置准备
用来测试的话,我们需要:
add.html、index.html、login.html和一个Controller类
<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Title</title></head><body>add</body></html>
<!DOCTYPE html><html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"><head><meta charset=\"UTF-8\"><title>Title</title></head><body>首页<br><a th:href=\"@{/add}\">add</a><br></body></html>
<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Title</title></head><body><form action=\"\">姓名:<input type=\"text\" name=\"username\"><br>密码:<input type=\"text\" name=\"password\"><br><input type=\"submit\" value=\"提交\"></form></body></html>
@Controllerpublic class ShiroController {@RequestMapping({\"/\",\"/index.html\"})public String index(){return \"index\";}@RequestMapping(\"/add\")public String add(){return \"add\";}@RequestMapping(\"/toLogin\")public String login(){return \"login\";}}
2.配置
上述文件页面搭建好后,测试运行应该是没什么问题的,我们开始在ShiroConfig类中配置
@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier(\"defaultWebSecurityManager\") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);//添加shiro的内置过滤器/** anon:无需认证就可以访问* authc:必须认证了才能访问* user:必须拥有 记住我 功能才能用* perms:拥有对某个资源的权限才能访问* role:拥有某个角色权限才能访问*/Map<String, String> map = new LinkedHashMap<>();map.put(\"/add\",\"authc\");map.put(\"/update\",\"authc\");bean.setFilterChainDefinitionMap(map);//设置登录请求bean.setLoginUrl(\"/toLogin\");return bean;}
这时候,如果没有权限,点击add会自动进入设置的登陆页面。
3.用户权限添加
添加登录Controller
@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\";} c15b0atch (UnknownAccountException uae) {model.addAttribute(\"msg\",\"用户名错误\");return \"login\";} catch (IncorrectCredentialsException ice) {model.addAttribute(\"msg\",\"密码错误\");return \"login\";}}
修改UserRealm
//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//用户名密码 先用下面的测试,之后可以连接数据库String name=\"root\";String password=\"123456\";UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken;if (!Token.getUsername().equals(name)){return null; //自动抛出异常}//密码认证 shiro来做return new SimpleAuthenticationInfo(\"\",password,\"\");}
修改登录页面
<p th:text=\"${msg}\"></p><form th:action=\"@{/Login}\">姓名:<input type=\"text\" name=\"username\"><br>密码:<input type=\"text\" name=\"password\"><br><input type=\"submit\" value=\"提交\"></form>
之后就可以去测试了。
整合Mybatis
1.导入依赖
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</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.3</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency>
2.配置文件
application.yml:
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 148729url: jdbc:mysql://localhost:3306/mybatistest?serverTime=UTC&useUnicode=true&characterEncoding=utf-8#druid配置#配置初始化大小/最小/最大initialSize: 5minIdle: 5maxActive: 20#获取连接等待超时时间maxWait: 60000#间隔多久进行一次检测,检测需要关闭的空闲连接timeBetweenEvictionRunsMillis: 60000#一个连接在池中最小生存的时间minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: false#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为falsepoolPreparedStatements: falsemaxPoolPreparedStatementPerConnectionSize: 20#监控统计拦截的filters,stat:监控统计;log4j:日志记录;wall:防御sql注入;如果启用log4j记得添加依赖filters: stat,wall,log4juseGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
application.properties:
mybatis.type-aliases-package=com.zc.pojomybatis.mapper-locations=classpath:mapper/*.xml
3.实体类
public class User {private Integer id;private String name;private String pwd;// Get/Set方法// toString()方法// 有参/无参方法}
4.创建mapper接口
创建一个mapper文件夹,UserMapper:
@Mapper@Repositorypublic interface UserMapper {User queryUserByName(String name);}
5.mapper.xml
这个文件的位置按照之前配置:
在resources/mapper下
mybatis.mapper-locations=classpath:mapper/*.xml
<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE mapperPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\"><mapper namespace=\"com.zc.mapper.UserMapper\"><select id=\"queryUserByName\" resultType=\"User\">select * from user where name=#{name};</select></mapper>
5.修改UserRealm
//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken; //用户名密码User user = userMapper.queryUserByName(Token.getUsername());String name=user.getName();String password=user.getPwd();if (user==null){return null;}//密码认证 shiro来做return new SimpleAuthenticationInfo(\"\",password,\"\");}
就可以进行测试了。
授权、权限管理
创建一个未经授权无法访问Controlle方法:
@RequestMapping(\"/noauth\")@ResponseBodyp15a9ublic String unauthorized(){return \"未经授权无法访问此页面\";}
修改ShiroConfig:
@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier(\"defaultWebSecurityManager\") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);Map<String, String> map = new LinkedHashMap<>();//拦截map.put(\"/add\",\"authc\");map.put(\"/update\",\"authc\");//授权map.put(\"/add\",\"perms[user:add]\");bean.setFilterChainDefinitionMap(map);//设置登录请求bean.setLoginUrl(\"/toLogin\");//拦截页面bean.setUnauthorizedUrl(\"/noauth\");return bean;}
测试
这个时候我已经给/add方法添加了user:add权限,只有有这个权限的用户才可以登录
这时候测试的话,会发现,即时登录成功通过了拦截,add页面也会显示未经授权无法访问此页面
在UserRealm中授权
// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addStringPermission(\"user:add\");return info;}
现在,只要登陆的用户,都会被添加user:add权限。
也可以进行数据库连接:
// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 数据库中加入一个权限字段,可以这样查找Subject subject = SecurityUtils.getSubject();//认证部分传入,可以获取User principal = (User) subject.getPrincipal();info.addStringPermission(user.getxxx());return info;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {UsernamePasswordToken Token = (UsernamePasswordToken) authenticationToken; //用户名密码User user = userMapper.queryUserByName(Token.getUsername());String password=user.getPwd();if (user==null){return null;}//这里修改了,传入了user,在授权部分可以获取return new SimpleAuthenticationInfo(user,password,\"\");}
整合Thymeleaf
1.导入依赖
<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency>
2.修改index.html
导入头文件
<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\" xmlns:shiro=\"http://www.thymeleaf.org/thymeleaf-extras-shiro\" >
内部文件
<body>首页<br><a th:href=\"@{/toLogin}\">登录</a><br><div shiro:hasPermission=\"user:add\"><a th:href=\"@{/add}\">add</a><br></div><a th:href=\"@{/update}\">update</a></body>
这种,是最简单的页面整理,不同权限会显示不同页面,没有权限部分功能不展示。
个人博客为:
MoYu\’s HomePage
MoYu\’s Gitee Blog