springboot+shiro整合教程
进阶教程:
1. springboot+shiro+redis(单机redis版)整合教程
2. springboot+shiro+redis(集群redis版)整合教程
3. springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)
本教程整合环境: java8 maven
开发工具: idea
版本: springboot 1.5.15.RELEASE
注:
1.本教程数据操作是模拟数据库操作,并没有真正进行持久化,自行修改即可。
2.角色权限验证未实现,只实现基本的登录验证,自行扩展即可。
项目结构:
pom.xml:
<?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>webapp</groupId> <artifactId>springboot-shiro</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-shiro</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.15.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-web</artifactId> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </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>
application.yml:
server:
port: 1000
User.java:
package webapp.model; import lombok.Data; /** * Created by Administrator on 2018/9/5. */ @Data public class User { private Long id; private String userName; private String password; }
UserService.java:
package webapp.service; import webapp.model.User; /** * Created by Administrator on 2018/9/5. */ public interface UserService { User findOneByUserName(String userName); }
UserServiceImpl.java:
package webapp.service.impl; import org.springframework.stereotype.Service; import webapp.model.User; import webapp.service.UserService; /** * Created by Administrator on 2018/9/5. */ @Service public class UserServiceImpl implements UserService { @Override public User findOneByUserName(String userName) { User user = new User(); user.setId(1L); user.setUserName("007少侠"); user.setPassword("123456"); return user; } }
UserController.java:
package webapp.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import webapp.service.UserService; import java.io.Serializable; /** * Created by Administrator on 2018/9/5. */ @RestController @RequestMapping("/core/user") public class UserController { @Autowired private UserService userService; /** * 登录 * @param * @return */ @GetMapping("/login") public String login(String userName, String password) { System.out.println("登录" + userName); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(userName, password); subject.login(token); Session session = subject.getSession(); Serializable sessionId = session.getId(); System.out.println("登录成功 -> " + sessionId); return userName + "[" + sessionId + "]"; } @GetMapping("/logout") public String logout() { SecurityUtils.getSubject().logout(); return "退出登录成功"; } /** * 获取当前登录用户 * @return */ @GetMapping("/findUser") public String findUser() { Subject subject = SecurityUtils.getSubject(); PrincipalCollection collection = subject.getPrincipals(); if (null != collection && !collection.isEmpty()) { String userName = (String) collection.iterator().next(); System.out.println("获取当前登录用户" + userName); return userService.findOneByUserName(userName).toString(); } return "{\n" + " \"codeEnum\": \"OVERTIME\",\n" + " \"code\": 0,\n" + " \"data\": null,\n" + " \"msg\": \"未登陆/登陆超时\",\n" + " \"success\": false\n" + "}"; } }
下面为shiro基本配置:
UserShiroRealm.java:
package webapp.shiro; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.eis.SessionDAO; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.springframework.beans.factory.annotation.Autowired; import webapp.model.User; import webapp.service.UserService; import java.util.Collection; /** * Created by Administrator on 2018/9/5. */ public class UserShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private SessionDAO sessionDAO; /** * 角色权限和对应权限添加 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } /** * 用户认证 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //加这一步的目的是在Post请求的时候会先进认证,然后在到请求 if (authenticationToken.getPrincipal() == null) { return null; } String userName = authenticationToken.getPrincipal().toString(); //只允许同一账户单个登录 Subject subject = SecurityUtils.getSubject(); Session nowSession = subject.getSession(); Collection<Session> sessions = sessionDAO.getActiveSessions(); if(sessions != null && sessions.size() > 0) { for (Session session : sessions) { if (!nowSession.getId().equals(session.getId()) && (session.getTimeout() == 0 || userName.equals(String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY))))) { sessionDAO.delete(session); } } } User user = userService.findOneByUserName(userName); if (user == null) { return null; } else { //这里验证authenticationToken和simpleAuthenticationInfo的信息 return new SimpleAuthenticationInfo(userName, user.getPassword(), getName()); } } }
ShiroCoreController.java:
package webapp.shiro; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * Created by Administrator on 2018/9/5. */ @RestController public class ShiroCoreController { @GetMapping("/loginUnAuth") public String loginUnAuth() { return "{\n" + " \"codeEnum\": \"OVERTIME\",\n" + " \"code\": 0,\n" + " \"data\": null,\n" + " \"msg\": \"未登陆/登陆超时\",\n" + " \"success\": false\n" + "}"; } @GetMapping("/authorUnAuth") public String authorUnAuth() { return "{\n" + " \"codeEnum\": \"ERR_PERMISSIONS\",\n" + " \"code\": -2,\n" + " \"data\": null,\n" + " \"msg\": \"无此权限\",\n" + " \"success\": false\n" + "}"; } }
shiro配置类ShiroConfiguration.java:
package webapp.conf; import org.apache.shiro.cache.MemoryConstrainedCacheManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.eis.MemorySessionDAO; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import webapp.shiro.UserShiroRealm; import java.util.HashMap; /** * shiro配置类 * Created by Administrator on 2018/9/5. */ @Configuration public class ShiroConfiguration { //将自己的验证方式加入容器 @Bean public UserShiroRealm userShiroRealm() { return new UserShiroRealm(); } @Bean public MemorySessionDAO getMemorySessionDAO() { return new MemorySessionDAO(); } @Bean public SimpleCookie getSimpleCookie() { SimpleCookie simpleCookie = new SimpleCookie(); simpleCookie.setName("SHRIOSESSIONID"); return simpleCookie; } @Bean public MemoryConstrainedCacheManager memoryConstrainedCacheManager() { return new MemoryConstrainedCacheManager(); } //配置shiro session 的一个管理器 @Bean public DefaultWebSessionManager getDefaultWebSessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(getMemorySessionDAO()); sessionManager.setGlobalSessionTimeout(-1000); //session有效期 默认值1800000 30分钟 1800000毫秒 -1000表示永久 SimpleCookie simpleCookie = getSimpleCookie(); simpleCookie.setHttpOnly(true); //设置js不可读取此Cookie simpleCookie.setMaxAge(3 * 365 * 24 * 60 * 60); //3年 cookie有效期 sessionManager.setSessionIdCookie(simpleCookie); return sessionManager; } //配置核心安全事务管理器 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(userShiroRealm()); manager.setCacheManager(memoryConstrainedCacheManager()); manager.setSessionManager(getDefaultWebSessionManager()); return manager; } //加入注解的使用,不加入这个注解不生效 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } //Filter工厂,设置对应的过滤条件和跳转条件 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); HashMap<String, String> map = new HashMap<>(); //登出 //map.put("/logout", "logout"); //认证 /###/@@@/** map.put("/api/**", "authc"); //登录认证不通过跳转 shiroFilterFactoryBean.setLoginUrl("/loginUnAuth"); //首页 //shiroFilterFactoryBean.setSuccessUrl("/index"); //权限认证不通过跳转 shiroFilterFactoryBean.setUnauthorizedUrl("/authorUnAuth"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } }
启动项目,访问相关接口校验是否成功:
登录接口:http://localhost:1000/core/user/login?userName=002&password=123456
返回:002[42c6d423-e48e-4164-b17d-0cbc0f9ca832]
获取用户:http://localhost:1000/core/user/findUser
返回:User(id=1, userName=007少侠, password=123456)
退出登录:http://localhost:1000/core/user/logout
返回:退出登录成功