1.springboot整合shiro
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.2.1.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.qiao</groupId> 12 <artifactId>hello-shiro</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>hello-shiro</name> 15 <description>Demo project for Spring Boot</description> 16 17 <properties> 18 <java.version>1.8</java.version> 19 </properties> 20 21 <dependencies> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter-web</artifactId> 25 </dependency> 26 27 <dependency> 28 <groupId>org.springframework.boot</groupId> 29 <artifactId>spring-boot-starter-test</artifactId> 30 <scope>test</scope> 31 <exclusions> 32 <exclusion> 33 <groupId>org.junit.vintage</groupId> 34 <artifactId>junit-vintage-engine</artifactId> 35 </exclusion> 36 </exclusions> 37 </dependency> 38 <!-- shiro thymeleaf整合 --> 39 <dependency> 40 <groupId>com.github.theborakompanioni</groupId> 41 <artifactId>thymeleaf-extras-shiro</artifactId> 42 <version>2.0.0</version> 43 </dependency> 44 <dependency> 45 <groupId>org.projectlombok</groupId> 46 <artifactId>lombok</artifactId> 47 </dependency> 48 <!-- 引入mysql驱动包 --> 49 <dependency> 50 <groupId>mysql</groupId> 51 <artifactId>mysql-connector-java</artifactId> 52 <version>5.1.27</version> 53 </dependency> 54 <!-- 引入Druid依赖,阿里巴巴所提供的数据源 --> 55 <dependency> 56 <groupId>com.alibaba</groupId> 57 <artifactId>druid</artifactId> 58 <version>1.0.29</version> 59 </dependency> 60 <dependency> 61 <groupId>org.mybatis.spring.boot</groupId> 62 <artifactId>mybatis-spring-boot-starter</artifactId> 63 <version>2.1.0</version> 64 </dependency> 65 <dependency> 66 <groupId>org.springframework.boot</groupId> 67 <artifactId>spring-boot-starter-thymeleaf</artifactId> 68 </dependency> 69 <dependency> 70 <groupId>org.apache.shiro</groupId> 71 <artifactId>shiro-spring</artifactId> 72 <version>1.4.1</version> 73 </dependency> 74 </dependencies> 75 76 <build> 77 <plugins> 78 <plugin> 79 <groupId>org.springframework.boot</groupId> 80 <artifactId>spring-boot-maven-plugin</artifactId> 81 </plugin> 82 83 </plugins> 84 85 </build> 86 87 </project>
其中shiro整合spring的依赖是
1 <dependency> 2 <groupId>org.apache.shiro</groupId> 3 <artifactId>shiro-spring</artifactId> 4 <version>1.4.1</version> 5 </dependency>
shiro和thymeleaf整合的依赖是
1 <dependency> 2 <groupId>com.github.theborakompanioni</groupId> 3 <artifactId>thymeleaf-extras-shiro</artifactId> 4 <version>2.0.0</version> 5 </dependency>
2.shiro核心的API
1.Subject:主体,当前用户,与当前应用进行交互。所有Subject都要绑定到SercurityManager。
2.SecurityManager:安全管理器 ,管理所有的Subject,所有关于安全的操作都会与之交互。
3.Realm:域,Shiro从Realm获取授权,认证,安全管理器认证用户身份就需要从Realm获取用户进行比较。
使用Shiro需要自定义Realm继承AuthorizingRealm
覆写两个方法即可
AuthenticationInfo 用于认证
AuthorizationInfo 用于授权
3.使用shiro
配置ShiroConfig
1 @Configuration 2 public class ShiroConfig { 3 //ShiroFilterFactoryBean 3. 4 @Bean 5 public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){ 6 ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); 7 //设置安全管理器 8 bean.setSecurityManager(securityManager); 9 //添加shiro的内置管理器 10 /** 11 * anon:无需认证即可访问 12 * authc:必须认证才能访问 13 * user:必须拥有 记住我 功能 14 * perms:拥有对某个资源的权限才能访问 15 * role:拥有某个角色权限才能访问 16 * filter.put("/user/add", "authc"); 17 * filter.put("/user/update", "authc"); 18 */ 19 //拦截 20 Map<String, String> filter = new LinkedHashMap<>(); 21 //授权 必须是user:add权限的才能访问 22 filter.put("/user/add", "perms[user:add]"); 23 //授权 必须是user:add权限的才能访问 24 filter.put("/user/update", "perms[user:update]"); 25 filter.put("/user/*", "authc"); 26 bean.setFilterChainDefinitionMap(filter); 27 //设置登录的请求 28 bean.setLoginUrl("/toLogin"); 29 //设置未授权请求页面 30 bean.setUnauthorizedUrl("/unAuthor"); 31 return bean; 32 } 33 34 //DefaultWebSecurityManager 2. 35 @Bean(name = "securityManager") 36 public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm){ 37 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 38 //关联UserRealm 39 securityManager.setRealm(userRealm); 40 return securityManager; 41 } 42 43 //创建realm类,自定义 44 @Bean 45 public UserRealm userRealm(){ 46 return new UserRealm(); 47 } 48 49 //整合shiroDialect 用于thymeleaf和shiro标签配合使用 50 @Bean 51 public ShiroDialect shiroDialect(){ 52 return new ShiroDialect(); 53 } 54 }
自定义UserRealm
1 public class UserRealm extends AuthorizingRealm { 2 @Autowired 3 UserService userService; 4 //授权 5 @Override 6 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 7 System.out.println("执行授权==》doGetAuthorizationInfo"); 8 9 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 10 11 //拿到当前登录的这个对象 12 Subject subject = SecurityUtils.getSubject(); 13 User currentUser = (User) subject.getPrincipal(); 14 //如果没有权限,权限为null,就返回null 15 if (currentUser.getPerms() == null){ 16 return null; 17 } 18 //设置权限~从数据库查寻 19 info.addStringPermission(currentUser.getPerms()); 20 return info; 21 } 22 //认证 23 @Override 24 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 25 throws AuthenticationException { 26 System.out.println("执行认证==》AuthenticationInfo"); 27 //连接真实的数据库 28 UsernamePasswordToken userToken = (UsernamePasswordToken) token; 29 User user = userService.queryUserByName(userToken.getUsername()); 30 //把登录用户塞进shiro的session shiro有自己独立的session~这也是为什么shiro可以脱离web使用 31 Subject subject = SecurityUtils.getSubject(); 32 Session session = subject.getSession(); 33 session.setAttribute("loginUser", user); 34 if(user == null){ 35 throw new UnknownAccountException(); 36 } 37 //盐值 38 String saltPassword = ShiroUtil.saltPassword(userToken.getPassword(), user.getSaltValue()); 39 // userToken.getPassword() 获取用户输入的密码 40 if (!user.getPwd().equals(saltPassword)){ 41 throw new IncorrectCredentialsException(); 42 } 43 //密码认证,由shiro做~ 为了防止密码泄露 第4个参数是realm名称 44 return new SimpleAuthenticationInfo(user, userToken.getPassword(), ByteSource.Util.bytes(user.getSaltValue()), this.getName()); 45 } 46 }
需要密码加密,创建工具类ShiroUtil
1 public class ShiroUtil { 2 public static String createSalt(){ 3 //生成32的随机盐值 4 return UUID.randomUUID().toString().replaceAll("-", ""); 5 } 6 /** 7 * @param srcPwd 原始密码 8 * @param saltValue 盐值 9 */ 10 public static String saltPassword(Object srcPwd, String saltValue){ 11 return new SimpleHash("MD5", srcPwd, saltValue, 1024).toString(); 12 } 13 }
创建实体类User
1 @Data 2 @AllArgsConstructor 3 @NoArgsConstructor 4 public class User { 5 private Integer id; 6 private String name; 7 private String pwd; 8 private String perms; 9 /** 10 * 盐值 11 */ 12 private String saltValue; 13 14 }
我这里使用的mybatis,创建UserMapper接口,直接使用注解写sql(不喜欢XML)
1 @Repository 2 @Mapper 3 public interface UserMapper { 4 @Select("select * from mybatis.user where name = #{name}") 5 public User queryUserByName(String name); 6 @Insert("insert into mybatis.user(name,pwd,perms,saltValue) values(#{name},#{pwd},#{perms},#{saltValue})") 7 public void insertUser(User user); 8 }
创建Service接口和实现类,用于获取数据
1 public interface UserService { 2 public User queryUserByName(String name); 3 public void insertUser(User user); 4 }
1 @Service 2 public class UserServiceImpl implements UserService { 3 @Autowired 4 UserMapper userMapper; 5 @Override 6 public User queryUserByName(String name) { 7 return userMapper.queryUserByName(name); 8 } 9 10 @Override 11 public void insertUser(User user) { 12 userMapper.insertUser(user); 13 } 14 }
创建一个简陋的主页、登录和注册页面
index.html
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org" 3 xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>首页</title> 7 </head> 8 <body> 9 <h1>首页</h1> 10 <div th:if="${session.loginUser==null}"> 11 <a th:href="@{/toLogin}">登录</a> 12 </div> 13 <div th:if="${session.loginUser==null}"> 14 <a th:href="@{/toRegister}">注册</a> 15 </div> 16 <div th:if="${session.loginUser!=null}"> 17 <a th:href="@{/logout}">退出</a> 18 </div> 19 <p>hello,shiro</p> 20 <div shiro:hasPermission="user:add"> 21 <a th:href="@{/user/add}">add</a> 22 </div> 23 <div shiro:hasPermission="user:update"> 24 <a th:href="@{/user/update}">update</a> 25 </div> 26 </body> 27 </html>
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro" 在thymeleaf中使用shiro标签的命名空间
login.html
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录</title> 6 </head> 7 <body> 8 <p th:text="${msg}" style="color: red;"></p> 9 <form th:action="@{/login}" method="post"> 10 <p>用户名:<input type="text" name="username"></p> 11 <p>密 码:<input type="text" name="password"></p> 12 <input type="submit"> 13 </form> 14 </body> 15 </html>
register.html
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>注册</title> 6 </head> 7 <body> 8 <h1>注册</h1> 9 <form th:action="@{/register}" method="post"> 10 <p>用户名:<input type="text" name="username"></p> 11 <p>密 码:<input type="text" name="password"></p> 12 <input type="submit"> 13 </form> 14 </body> 15 </html>
yml配置文件
1 spring: 2 datasource: 3 username: root 4 password: 123456 5 #?serverTimezone=UTC解决时区的报错 6 url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 7 driver-class-name: com.mysql.jdbc.Driver 8 type: com.alibaba.druid.pool.DruidDataSource 9 10 #Spring Boot 默认是不注入这些属性值的,需要自己绑定 11 #druid 数据源专有配置 12 initialSize: 5 13 minIdle: 5 14 maxActive: 20 15 maxWait: 60000 16 timeBetweenEvictionRunsMillis: 60000 17 minEvictableIdleTimeMillis: 300000 18 validationQuery: SELECT 1 FROM DUAL 19 testWhileIdle: true 20 testOnBorrow: false 21 testOnReturn: false 22 poolPreparedStatements: true 23 24 #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 25 #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority 26 #则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j 27 filters: stat,wall,log4j 28 maxPoolPreparedStatementPerConnectionSize: 20 29 useGlobalDataSourceStat: true 30 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
数据表
非原创,仅是在学习西部狂神-秦疆的课程的基础上加了撒盐加密的功能
附上参考代码,继续努力~ https://github.com/Sevenwsq/springboot-shiro.git