什么是shiro
shiro是一个Java平台的开源权限框架,用于认证和访问授权。具体来说,满足对如下元素的支持:
- 用户,角色,权限(仅仅是操作权限,数据权限必须与业务需求紧密结合),资源(url)
- 用户分配角色,角色定义权限
- 访问授权时支持角色或者权限,并且支持多级的权限定义
1.数据库设计
数据库中有分别有6个表,分别是:权限表(permissions)、用户表(users)、用户权限关系表(user_role)、角色表(roles)、角色权限关系表(role_permission)、shiro过滤器表( resource)
数据库下载地址: https://pan.baidu.com/s/1AMKwNfRimPPzqWHY30S1HQ 提取码: r9oh
2.创建springboot项目并在pom加入依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 导入web支持:SpringMVC开发支持,Servlet相关的程序 --> <!-- web支持,SpringMVC, Servlet支持等 web 启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--导入thymeleaf依赖--> <!--thymeleaf启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- jasper --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <!-- fastjson阿里巴巴jSON处理器 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!-- reids --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- shiro+redis缓存插件 --> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>2.4.6</version> </dependency> <!-- mysql 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- druid 数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.9</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.0</version> </dependency> <!-- thymeleaf整合shiro标签 --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>
3. 编辑application.yml
#springboot server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/shiro?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8 username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver redis: host: localhost port: 6379 jedis: pool: max-idle: 8 min-idle: 0 max-active: 8 max-wait: -1 timeout: 0 mybatis: mapper-locations: classpath:mapper/*.xml #*/ type-aliases-package: com.jbl.springboot_redis_shiro.entity
4.建包搭架子(先把包建完)
4.1创建实体类
/** * @Author: yfj * @Description: 资源 * Serializable 要缓存的JavaBean必须实现Serializable接口,因为Spring会将对象先序列化再存入 Redis */ @Data public class Resources implements Serializable { public Integer id; public String key; public String val; public Integer order; } /** * @Author: yfj * @Description: 用户信息 * Serializable 要缓存的JavaBean必须实现Serializable接口,因为Spring会将对象先序列化再存入 Redis */ @Data public class User implements Serializable { public Integer u_id; public String u_name; public String u_pwd; }
我用的lombok插件,简化get、set、ToString等方法想学习的可以点击下面链接:
4.2springboot启动类
@SpringBootApplication @MapperScan("com.jbl.springboot_redis_shiro.dao") //这里填的是自己dao层接口的路径 public class SpringbootRedisShiroApplication { public static void main(String[] args) { SpringApplication.run(SpringbootRedisShiroApplication.class, args); } }
4.3启动springboot自动跳转到登陆页面
/** * 启动项目自动跳转到登陆页面 */ @SpringBootConfiguration public class AutoStartProjectInDefaultBrowser implements CommandLineRunner { @Value("${server.port}") private String port; @Override public void run(String ... args) throws Exception { try { Runtime.getRuntime().exec("cmd /c start http://localhost:" + port + "/shiro/login"); } catch (Exception ex) { ex.printStackTrace(); } } }
4.4全局异常类
/** * @author yfj * @version 1.0 * @date 2020/04/16 下午 03:11 * @description: 全局异常类 */ @ControllerAdvice public class CtrlExceptionHandler { private static Logger logger = LoggerFactory.getLogger(CtrlExceptionHandler.class); //拦截未授权页面 @ResponseStatus(value = HttpStatus.FORBIDDEN) @ExceptionHandler(UnauthorizedException.class) public String handleException(UnauthorizedException e) { logger.debug(e.getMessage()); return "403"; } @ResponseStatus(value = HttpStatus.FORBIDDEN) @ExceptionHandler(AuthorizationException.class) public String handleException2(AuthorizationException e) { logger.debug(e.getMessage()); return "403"; } }
5.后台代码
5.1 Mapper.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.jbl.springboot_redis_shiro.dao.ShiroMapper"> <select id="login" resultType="User" parameterType="String"> SELECT `u_id`,`u_name`,`u_pwd` FROM `users` WHERE `u_name`= #{name} and `u_pwd` = #{u_pwd} </select> <select id="seRoles" resultType="String" parameterType="String"> SELECT r.`r_name` FROM `user_role` ur LEFT JOIN `users` u ON(ur.`u_id`=u.`u_id`) LEFT JOIN `roles` r ON(ur.`r_id`=r.`r_id`) WHERE u.`u_name`=#{userName} </select> <select id="orders" resultType="Resources"> SELECT * FROM `resource` ORDER BY `orders` </select> <select id="quanxian" resultType="String" parameterType="String"> SELECT p.`p_name` FROM `role_permission` rp LEFT JOIN `permissions` p ON(rp.`p_id`=p.`p_id`) LEFT JOIN `roles` r ON(rp.`r_id`=r.`r_id`) WHERE r.`r_name`IN(SELECT r.`r_name` FROM `user_role` ur LEFT JOIN `users` u ON(ur.`u_id`=u.`u_id`) LEFT JOIN `roles` r ON(ur.`r_id`=r.`r_id`) WHERE u.`u_name`=#{userName}) </select> </mapper>
5.2 Mapper接口
import com.jbl.springboot_redis_shiro.entity.Resources; import com.jbl.springboot_redis_shiro.entity.User; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; import java.util.Set; /** * @author yfj * @version 1.0 * @date 2020/04/15 下午 02:40 * @description: */ @Repository public interface ShiroMapper { /** * 登陆 * @param name 用户名 * @param u_pwd 密码 * @return */ User login(@Param("name")String name,@Param("u_pwd")String u_pwd); /** * 查询资源 * @return List */ List<Resources> orders(); /** * 根据用户查询角色 * @param userName * @return */ Set<String> seRoles(@Param("userName")String userName); /** * 根据名字查询权限 * @param userName * @return */ Set<String> quanxian(@Param("userName")String userName); }
5.3 server接口
import com.jbl.springboot_redis_shiro.entity.Resources; import com.jbl.springboot_redis_shiro.entity.User; import java.util.List; import java.util.Set; /** * @author yfj * @version 1.0 * @date 2020/04/15 下午 02:42 * @description: */ public interface ShiroServer { /** * 登陆 * @return */ User login(String name,String u_Pwd); /** * 查询资源 * @return List */ List<Resources> orders(); /** * 根据用户查询角色 * @param userName * @return */ Set<String> seRoles(String userName); /** * 根据名字查询权限 * @param userName * @return */ Set<String> quanxian(String userName); }
server实现类
import com.jbl.springboot_redis_shiro.dao.ShiroMapper; import com.jbl.springboot_redis_shiro.entity.Resources; import com.jbl.springboot_redis_shiro.entity.User; import com.jbl.springboot_redis_shiro.server.ShiroServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Set; /** * @author yfj * @version 1.0 * @date 2020/04/15 下午 02:43 * @description: */ @Service("ShiroServerImpl") public class ShiroServerImpl implements ShiroServer{ @Autowired private ShiroMapper shiroMapper; @Override public User login(String name, String u_Pwd) { return shiroMapper.login(name,u_Pwd); } @Override public List<Resources> orders() { return shiroMapper.orders(); } @Override public Set<String> seRoles(String userName) { return shiroMapper.seRoles(userName); } @Override public Set<String> quanxian(String userName) { return shiroMapper.quanxian(userName); } }
6.创建ShiroConfig类
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import com.jbl.springboot_redis_shiro.server.ShiroServer; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; 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.session.mgt.DefaultWebSessionManager; import org.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * @author yfj * @version 1.0 * @date 2020/04/15 下午 07:06 * @description: shiro配置 */ @Configuration public class ShiroConfig { @Autowired private ShiroServer shiroServer; @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); // 没有登陆的用户只能访问登陆页面 shiroFilterFactoryBean.setLoginUrl("/shiro/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/shiro/index"); // 未授权界面; ----这个配置了没卵用,具体原因想深入了解的可以自行百度 shiroFilterFactoryBean.setUnauthorizedUrl("/shiro/wuquan"); //自定义拦截器 Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>(); //限制同一帐号同时在线的个数。 filtersMap.put("kickout", kickoutSessionControlFilter()); shiroFilterFactoryBean.setFilters(filtersMap); // 权限控制map. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); /* 我加了下面这段代码启动就报错,如果有人知道是为什么请联系我,谢谢 List<Resources> list=shiroServer.orders(); for(Resources itms:list){ filterChainDefinitionMap.put(itms.getKey(),itms.getVal()); }*/ //添加Shiro内置过滤器 /** * Shiro内置过滤器,可以实现权限相关的拦截器 * 常用的过滤器: * anon: 无需认证(登录)可以访问 * authc: 必须认证才可以访问 * user: 如果使用rememberMe的功能可以直接访问 * perms: 该资源必须得到资源权限才可以访问 * role: 该资源必须得到角色权限才可以访问 */ filterChainDefinitionMap.put("/shiro/kickout", "anon"); filterChainDefinitionMap.put("/shiro/login", "anon"); filterChainDefinitionMap.put("/shiro/submitLogin", "anon"); filterChainDefinitionMap.put("/shiro/index", "authc"); filterChainDefinitionMap.put("/shiro/tianjia", "perms[insert]"); filterChainDefinitionMap.put("/shiro/update", "perms[update]"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(myShiroRealm()); // 自定义缓存实现 使用redis securityManager.setCacheManager(cacheManager()); // 自定义session管理 使用redis securityManager.setSessionManager(sessionManager()); return securityManager; } /** * 身份认证realm; (这个需要自己写,账号密码校验;权限等) * @return */ @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); return myShiroRealm; } /** * cacheManager 缓存 redis实现 * 使用的是shiro-redis开源插件 * * @return */ public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * 配置shiro redisManager * 使用的是shiro-redis开源插件 * * @return */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost("localhost"); redisManager.setPort(6379); redisManager.setExpire(1800);// 配置缓存过期时间 redisManager.setTimeout(0); // redisManager.setPassword(password); return redisManager; } /** * Session Manager * 使用的是shiro-redis开源插件 */ @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDAO()); return sessionManager; } /** * RedisSessionDAO shiro sessionDao层的实现 通过redis * 使用的是shiro-redis开源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } /** * 限制同一账号登录同时登录人数控制 * * @return */ @Bean public KickoutSessionControlFilter kickoutSessionControlFilter() { KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter(); kickoutSessionControlFilter.setCacheManager(cacheManager()); kickoutSessionControlFilter.setSessionManager(sessionManager()); kickoutSessionControlFilter.setKickoutAfter(false); kickoutSessionControlFilter.setMaxSession(1); kickoutSessionControlFilter.setKickoutUrl("/shiro/kickout"); return kickoutSessionControlFilter; } /*** * 授权所用配置 * * @return */ @Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } /*** * 使授权注解起作用不如不想配置可以在pom文件中加入 * <dependency> *<groupId>org.springframework.boot</groupId> *<artifactId>spring-boot-starter-aop</artifactId> *</dependency> * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * Shiro生命周期处理器 * */ @Bean public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用 */ @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); }
7.Realm(凭证认证、权限获取)
package com.jbl.springboot_redis_shiro.shiroconfig; import com.jbl.springboot_redis_shiro.entity.User; import com.jbl.springboot_redis_shiro.server.ShiroServer; 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.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; /** * @author yfj * @version 1.0 * @date 2020/04/15 下午 07:16 * @description: */ public class MyShiroRealm extends AuthorizingRealm { private static org.slf4j.Logger logger = LoggerFactory.getLogger(MyShiroRealm.class); @Autowired private ShiroServer shiroServer; /** * 认证信息.(身份验证) : Authentication 是用来验证用户身份 * */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { logger.info("---------------- 执行 Shiro 凭证认证 ----------------------"); UsernamePasswordToken token = (UsernamePasswordToken) authcToken; String name = token.getUsername(); String password = String.valueOf(token.getPassword()); // 从数据库获取对应用户名密码的用户 User userList = shiroServer.login(name,password); if (userList == null) { //用户名不存在 return null; // shiro底层会抛出UnKnoAccountException 异常 } logger.info("---------------- Shiro 凭证认证成功 ----------------------"); //判断密码 /** * 第一个参数是否返回给controller里的login数据 * 第二个参数是数据库的密码 * 第三个参数是名字 */ return new SimpleAuthenticationInfo(userList,userList.getU_pwd(),""); } /** * 授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { logger.info("---------------- 执行 Shiro 权限获取 ---------------------"); Object principal = principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); if (principal instanceof User) { User userLogin = (User) principal; //根据名字查询角色 Set<String> roles = shiroServer.seRoles(userLogin.getU_name()); authorizationInfo.addRoles(roles); //根据名字查询权限 Set<String> permissions = shiroServer.quanxian(userLogin.getU_name()); authorizationInfo.addStringPermissions(permissions); } logger.info("---- 获取到以下权限 ----"); logger.info(authorizationInfo.getStringPermissions().toString()); logger.info("---------------- Shiro 权限获取成功 ----------------------"); return authorizationInfo; } }
8.限制并发人数登陆
package com.jbl.springboot_redis_shiro.shiroconfig; import com.alibaba.fastjson.JSON; import com.jbl.springboot_redis_shiro.entity.User; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.DefaultSessionKey; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.AccessControlFilter; import org.apache.shiro.web.util.WebUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; /** * @author yfj * @version 1.0 * @date 2020/04/15 下午 07:17 * @description: 限制并发人数登陆 */ public class KickoutSessionControlFilter extends AccessControlFilter { private String kickoutUrl; //踢出后到的地址 private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户 private int maxSession = 1; //同一个帐号最大会话数 默认1 private SessionManager sessionManager; private Cache<String, Deque<Serializable>> cache; public void setKickoutUrl(String kickoutUrl) { this.kickoutUrl = kickoutUrl; } public void setKickoutAfter(boolean kickoutAfter) { this.kickoutAfter = kickoutAfter; } public void setMaxSession(int maxSession) { this.maxSession = maxSession; } public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; } //设置Cache的key的前缀 public void setCacheManager(CacheManager cacheManager) { this.cache = cacheManager.getCache("shiro_redis_cache"); } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return false; } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { Subject subject = getSubject(request, response); if(!subject.isAuthenticated() && !subject.isRemembered()) { //如果没有登录,直接进行之后的流程 return true; } Session session = subject.getSession(); User user = (User) subject.getPrincipal(); String username = user.getU_name(); Serializable sessionId = session.getId(); //读取缓存 没有就存入 Deque<Serializable> deque = cache.get(username); //如果此用户没有session队列,也就是还没有登录过,缓存中没有 //就new一个空队列,不然deque对象为空,会报空指针 if(deque==null){ deque = new LinkedList<Serializable>(); } //如果队列里没有此sessionId,且用户没有被踢出;放入队列 if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) { //将sessionId存入队列 deque.push(sessionId); //将用户的sessionId队列缓存 cache.put(username, deque); } //如果队列里的sessionId数超出最大会话数,开始踢人 while(deque.size() > maxSession) { Serializable kickoutSessionId = null; if(kickoutAfter) { //如果踢出后者 kickoutSessionId = deque.removeFirst(); //踢出后再更新下缓存队列 cache.put(username, deque); } else { //否则踢出前者 kickoutSessionId = deque.removeLast(); //踢出后再更新下缓存队列 cache.put(username, deque); } try { //获取被踢出的sessionId的session对象 Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId)); if(kickoutSession != null) { //设置会话的kickout属性表示踢出了 kickoutSession.setAttribute("kickout", true); } } catch (Exception e) {//ignore exception } } //如果被踢出了,直接退出,重定向到踢出后的地址 if (session.getAttribute("kickout") != null) { //会话被踢出了 try { //退出登录 subject.logout(); } catch (Exception e) { //ignore } saveRequest(request); Map<String, String> resultMap = new HashMap<String, String>(); //判断是不是Ajax请求 if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"))) { resultMap.put("user_status", "300"); resultMap.put("message", "您已经在其他地方登录,请重新登录!"); //输出json串 out(response, resultMap); }else{ //重定向 WebUtils.issueRedirect(request, response, kickoutUrl); } return false; } return true; } private void out(ServletResponse hresponse, Map<String, String> resultMap) throws IOException { try { hresponse.setCharacterEncoding("UTF-8"); PrintWriter out = hresponse.getWriter(); out.println(JSON.toJSONString(resultMap)); out.flush(); out.close(); } catch (Exception e) { System.err.println("KickoutSessionFilter.class 输出JSON异常,可以忽略。"); } } }
9.Controller层
import com.jbl.springboot_redis_shiro.server.ShiroServer; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; /** * @author yfj * @version 1.0 * @date 2020/04/15 下午 02:37 * @description: 控制层 */ @RequestMapping("/shiro") @Controller public class ControllerDemo { @Autowired private ShiroServer shiroServer; @RequestMapping("/login") public String login(){ return "login"; } //被踢出后跳转的页面 @RequestMapping("/kickout") public String kickout(){ return "kickout"; } @RequestMapping("/submitLogin") public String submitLogin(String username, String password, Model model) { //加密格式 String hashAlgorithmName="MD5"; //用户输入的密码 String credentials =password; //盐值 Object salt = ByteSource.Util.bytes (username) ; //加密次数 int hashIterations = 1024; //加密 Object result =new SimpleHash(hashAlgorithmName,credentials, salt, hashIterations); System.out.println(result); try { //使用shiro 编写认证操作 UsernamePasswordToken token = new UsernamePasswordToken(username, String.valueOf(result)); //拿到subject Subject subject = SecurityUtils.getSubject(); // 执行登陆方法 subject.login(token); // 执行到这里说明用户已登录成功 return "redirect:/shiro/index"; }catch (IncorrectCredentialsException e) { model.addAttribute("msg", "密码错误"); return "login"; }catch (UnknownAccountException e){ //如果有登陆失败就会跳入这里 model.addAttribute("msg","用户名不存在"); //UnknownAccountException(用户不存在) return "login"; } } @RequestMapping("/tianjia") public String tianjia(){ return "user/tianjai"; } @RequestMapping("/update") public String update(){ return "user/update"; } @RequestMapping("/index") public String index(){ return "user/index"; } @RequestMapping("/wuquan") public String wuquan(){ return "yichang"; } }
10.页面
1.index.html (主页面)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div shiro:hasPermission="insert"> <a href="/shiro/tianjia">进入用户添加功能</a><br/> </div> <div shiro:hasPermission="update"> <a href="/shiro/update">进入用户修改功能</a><br/> </div> <div shiro:hasPermission="select"> <a>显示</a> </div> </body> </html>
2.tianjii.html(添加页面)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>添加</h1> </body> </html>
3.update (修改页面)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>修改</h1> </body> </html>
4.异常页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>异常页面</h1> </body> </html>
5.多台设备登陆此账号提醒页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>多台设备登陆此账号提醒页面</h1> </body> </html>
6.登陆页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陆页面</title> </head> <body> <font style="color:red" th:text="${msg}"></font> <form action="/shiro/submitLogin" method="post"> 用户名:<input type="text" name="username"/><br/> 密码:<input type="text" name="password"/><br/> <input type="submit" value="登陆"/> </form> </body> </html>
7.无权限访问提醒页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>你无权访问这个页面</h1> </body> </html>