写一个不花里胡哨的纯粹的Springboot+Shiro的入门小栗子
效果如图:
首页:有登录注册
先注册一个,然后登陆
登录,成功自动跳转到home页
home页:通过认证之后才可以进
代码部分:
依赖:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </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> <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-all --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.3.2</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Java配置类:
package com.example.demo.conf; import com.example.demo.auth.PermissionRealm; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import java.util.LinkedHashMap; /** * @program: boot-shiro * @description: * @author: 001977 * @create: 2018-07-17 18:22 */ @Configuration public class ShiroConfig { /** * 1. 配置SecurityManager * @return */ @Bean public DefaultWebSecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } /** * 2. 配置缓存 * @return */ @Bean public CacheManager cacheManager(){ EhCacheManager ehCacheManager = new EhCacheManager(); ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml"); return ehCacheManager; } /** * 3. 配置Realm * @return */ @Bean public AuthorizingRealm realm(){ PermissionRealm realm = new PermissionRealm(); HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); // 指定加密算法 matcher.setHashAlgorithmName("MD5"); // 指定加密次数 matcher.setHashIterations(10); // 指定这个就不会报错 matcher.setStoredCredentialsHexEncoded(true); realm.setCredentialsMatcher(matcher); return realm; } /** * 4. 配置LifecycleBeanPostProcessor,可以来自动的调用配置在Spring IOC容器中 Shiro Bean 的生命周期方法 * @return */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } /** * 5. 启用IOC容器中使用Shiro的注解,但是必须配置第四步才可以使用 * @return */ @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){ return new DefaultAdvisorAutoProxyCreator(); } /** * 6. 配置ShiroFilter * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(){ LinkedHashMap<String, String> map = new LinkedHashMap<>(); // 静态资源 map.put("/css/**", "anon"); map.put("/js/**", "anon"); // 公共路径 map.put("/login", "anon"); map.put("/register", "anon"); //map.put("/*", "anon"); // 登出,项目中没有/logout路径,因为shiro是过滤器,而SpringMVC是Servlet,Shiro会先执行 map.put("/logout", "logout"); // 授权 map.put("/user/**", "authc,roles[user]"); map.put("/admin/**", "authc,roles[admin]"); // everything else requires authentication: map.put("/**", "authc"); ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); // 配置SecurityManager factoryBean.setSecurityManager(securityManager()); // 配置权限路径 factoryBean.setFilterChainDefinitionMap(map); // 配置登录url factoryBean.setLoginUrl("/"); // 配置无权限路径 factoryBean.setUnauthorizedUrl("/unauthorized"); return factoryBean; } }
Realm类:
package com.example.demo.auth; import com.example.demo.common.entity.User; import com.example.demo.service.UserService; 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.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.Set; /** * @program: boot-shiro * @description: * @author: 001977 * @create: 2018-07-12 13:03 */ public class PermissionRealm extends AuthorizingRealm { @Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Object principal = principalCollection.getPrimaryPrincipal(); User user = (User) principal; Set<String> roles = new HashSet<>(); roles.add("user"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken uToken = (UsernamePasswordToken) authenticationToken; String username = uToken.getUsername(); String password = String.valueOf(uToken.getPassword()); User user = userService.login(new User(username,password)); if(user == null){ throw new AuthenticationException("用户名密码不存在"); } //认证的实体信息 Object principal = user; //从数据库获取的密码 Object hashedCredentials = user.getPassword(); //盐值 ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername()); //当前Realm对象的name,调用父类的getName方法 String realmName = getName(); SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName); return info; } }
Controller:
package com.example.demo.controller; import com.example.demo.common.TempStorage; import com.example.demo.common.entity.User; import com.example.demo.common.response.BaseResponse; import com.example.demo.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; /** * @program: boot-shiro * @description: * @author: 001977 * @create: 2018-07-12 13:02 */ @RestController public class SimpleController { @Autowired private UserService userService; @RequestMapping("/") public ModelAndView index(){ return new ModelAndView("index"); } @RequestMapping("/login") public BaseResponse<String> login(@RequestBody User user){ BaseResponse<String> response = new BaseResponse<>(0,"登陆成功"); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken( user.getUsername(), user.getPassword()); subject.login(token); response.setData("/home"); return response; } @RequestMapping("/register") public BaseResponse register(@RequestBody User user){ userService.addUser(user); return new BaseResponse(0,"注册成功"); } @RequestMapping("/home") public ModelAndView home(){ ModelAndView mv = new ModelAndView("home"); mv.addObject("users", TempStorage.getInstance().getMap()); return mv; } }
其余代码参见GitHub