shiro学习(五、springboot+shiro+mybatis+thymeleaf)
入门shiro(感觉成功了)首先感谢狂神,然后我就一本正经的复制代码了
项目结构
运行效果
数据库
<dependencies> <!-- thymeleaf-shiro整合包 --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <!--快速生成pojo的方法有关的lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <!-- 引入 myBatis,这是 MyBatis官方提供的适配 Spring Boot 的,而不是Spring Boot自己的--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- Subject 用户, SecurityManager 管理所有用户, Realm 连接数据,需要自定义 --> <!--shiro整合spring的包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--thymeleaf模板--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</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>
ShiroConfig.class
@Configuration public class ShiroConfig { //ShiroFilterFactoryBean 第三步 @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); /* 添加shiro的内置过滤器 anon:无须认证就可以访问 authc:必须认证了才可以访问 perms:拥有对某个资源的权限才能访问 role:拥有某个角色才可以访问 */ Map<String, String> filterMap = new LinkedHashMap(); // filterMap.put("/user/add","authc"); // filterMap.put("/user/update","authc"); filterMap.put("/user/add","perms[user:add]"); filterMap.put("/user/update","perms[user:update]"); filterMap.put("/user/*","authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); //设置跳转到登录页面 shiroFilterFactoryBean.setLoginUrl("/toLogin"); //设置到未授权页面 shiroFilterFactoryBean.setUnauthorizedUrl("/noauth"); return shiroFilterFactoryBean; } //DefaultWebSecurityManager 第二步 @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); return securityManager; } //创建realm 域对象,需要自定义 第一步 @Bean(name = "userRealm") public UserRealm getUserRealm(){ return new UserRealm(); } //shiro 整合thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } }
UserRealm.class
public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; @Override //授权 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("-----授权了-----AuthorizationInfo"); SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); // info.addStringPermission("user:add"); //从数据库查权限 Subject subject = SecurityUtils.getSubject(); User currentUser = (User) subject.getPrincipal();//其实就是拿认证成功的时候的那个user info.addStringPermission(currentUser.getPerms()); return info; } @Override //认证 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("-----认证了-----AuthenticationInfo"); UsernamePasswordToken userToken = (UsernamePasswordToken) token; //用户名,密码去数据库取 User user = userService.queryUserByUsername(userToken.getUsername()); if (user==null){ //没有这个人 return null; //其实就是抛出UnknownAccountException异常 } //之后密码认证,shiro 它自己会做 SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,user.getPassword(),""); Subject currentSubject = SecurityUtils.getSubject(); Session session = currentSubject.getSession(); session.setAttribute("loginUser",user); return info; } }
MyController.class
@Controller public 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); return "index"; } catch (UnknownAccountException e) { model.addAttribute("msg","用户名错误"); return "login"; }catch (IncorrectCredentialsException e){ model.addAttribute("msg","密码错误"); return "login"; } } //没授权 @RequestMapping("/noauth") @ResponseBody public String unauthorized(){ return "没经授权无法进入"; } //退出 @RequestMapping("/logout") public String logout(){ Subject currentUser = SecurityUtils.getSubject(); currentUser.logout(); System.out.println("退出了"); return "login"; } }
User.class
@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String username; private String password; private String perms; }
UserMapper.class
@Mapper @Repository public interface UserMapper { public User queryUserByUsername(String username); }
UserService.class
public interface UserService { public User queryUserByUsername(String username); }
UserServiceImpl.class
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User queryUserByUsername(String username) { return userMapper.queryUserByUsername(username); } }
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"> <mapper namespace="com.kuang.mapper.UserMapper"> <select id="queryUserByUsername" parameterType="String" resultType="User"> select * from user where username=#{username} </select> </mapper>
application.properties
mybatis.type-aliases-package=com.kuang.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
application.yml
spring: datasource: username: root password: root #?serverTimezone=UTC解决时区的报错 url: jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #Spring Boot 默认是不注入这些属性值的,需要自己绑定 #druid 数据源专有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority #则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
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>shiro</title> </head> <body> <h1>首页</h1> <div th:if="${session.loginUser==null}"> <a th:href="@{/toLogin}">登录</a> </div> <p th:text="${msg}"></p> <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 th:if="${session.loginUser}"> <p><a th:href="@{/logout}">退出</a></p> </div> </body> </html>
login.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>shiro登录</title> </head> <body> <div> <p th:text="${msg}" style="color: red"></p> <form method="get" th:action="@{/login}"> <p>用户名:<input type="text" name="username"></p> <p>密 码:<input type="text" name="password"></p> <p><input type="submit" value="登录"></p> </form> </div> </body> </html>
add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>增加</title> </head> <body> <h2>add</h2> </body> </html>
update.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>更新</title> </head> <body> <h2>update</h2> </body> </html>
总结一下:首先搭建好环境是关键:springboot--》MVC--》mybatis,然后进行shiro的过滤,认证,授权。。。
主要理解好
ShiroConfig
ShiroFilterFactoryBean
setFilterChainDefinitionMap()
DefaultWebSecurityManager
自定义域 UserRealm 继承 AuthorizingRealm
AuthorizationInfo SimpleAuthenticationInfo
AuthenticationInfo SimpleAuthorizationInfo
UsernamePasswordToken
SecurityUtils
getSession()
getSubject()
getPrincipal()
AuthenticationToken
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。