springboot整合shiro简单使用(查询数据库)
shiro主要实现认证和授权的问题
认证确认用户身份,授权对用户访问资源的权限做控制
建表语句
CREATE TABLE `user` ( id INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT '主键', `name` VARCHAR(255) COMMENT '用户名', `password` VARCHAR(255) COMMENT '密码' ) INSERT INTO `user`(`name`,`password`) VALUES('test','123456') CREATE TABLE `role` ( id INT(11) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '主键', role_name VARCHAR(255) DEFAULT NULL COMMENT '角色名称', user_id VARCHAR(255) DEFAULT NULL COMMENT '用户id' ) INSERT INTO `role`(role_name,user_id) VALUES('admin','1') CREATE TABLE permission ( id INT(11) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '主键', permission VARCHAR(255) DEFAULT NULL COMMENT '权限', role_id VARCHAR(255) DEFAULT NULL COMMENT '角色id' ) INSERT INTO permission(permission,role_id) VALUES ('create','1'), ('query','1')
代码部分
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> </parent> <groupId>com.java</groupId> <artifactId>shiro-service</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.6.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--springboot整合mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <!--mysql驱动5.6.17--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.4</version> </dependency> <!--页面模板依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--热部署依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
server.port=8001 logging.level.com.java=debug logging.level.web=debug spring.devtools.add-properties=false spring.datasource.driver-class-name=com.mysql.jdbc.Driver #spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_shiro?useUnicode=true&characterEncoding=utf-8&useSSL=false #spring.datasource.username=root #spring.datasource.password=root mybatis.mapper-locations=classpath:mapping/*.xml mybatis.configuration.map-underscore-to-camel-case=true spring.aop.proxy-target-class=true
<?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.java.mapper.PermissionMapper"> <insert id="addBatchPermission" parameterType="java.util.List"> INSERT INTO permission(permission,role_id) VALUES <foreach collection="list" item="item" separator=","> (#{item.permission},#{item.roleId}) </foreach> </insert> </mapper>
<?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.java.mapper.UserMapper"> <resultMap id="userMap" type="com.java.bean.User"> <result property="name" column="name"/> <result property="password" column="password"/> <collection property="roles" ofType="com.java.bean.Role"> <result property="roleName" column="role_name"/> <collection property="permissions" ofType="com.java.bean.Permission"> <result property="permission" column="permission"/> </collection> </collection> </resultMap> <select id="getListByBean" resultMap="userMap"> SELECT a.`name`,a.`password`,b.`role_name`,c.`permission` FROM `user` a INNER JOIN `role` b ON a.id=b.user_id INNER JOIN permission c ON b.id=c.role_id </select> </mapper>
package com.java; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author yourheart * @Description * @create 2022-05-03 20:30 */ @SpringBootApplication public class ShiroApplication { public static void main(String[] args) { SpringApplication.run(ShiroApplication.class,args); } }
package com.java.bean; import lombok.Data; /** * @author yourheart * @Description * @create 2022-06-15 1:54 */ @Data public class Permission { private Integer id; private String permission; private Integer roleId; } package com.java.bean; import lombok.Data; /** * @author yourheart * @Description * @create 2021-09-17 23:54 */ @Data public class ResponseBean { /** * 状态码 */ private String code; /** * 返回值 */ private String msg; /** * 拓展字段 */ private Object extraInfo; } package com.java.bean; import lombok.Data; import java.util.List; /** * @author yourheart * @Description * @create 2022-06-15 1:55 */ @Data public class Role { private Integer id; private String roleName; private Integer userId; private List<Permission> permissions; } package com.java.bean; import lombok.Data; import java.util.List; /** * @author yourheart * @Description * @create 2022-06-15 1:53 */ @Data public class User { private Integer id; private String name; private Integer password; private List<Role> roles; }
package com.java.mapper; import com.java.bean.Permission; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author yourheart * @Description * @create 2022-06-25 8:37 */ @Mapper public interface PermissionMapper { @Select("SELECT permission FROM permission WHERE role_id=#{roleId}") List<Permission> findPermission(Integer roleId); int addBatchPermission(List<Permission> permissions); } package com.java.mapper; import com.java.bean.Role; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author yourheart * @Description * @create 2022-06-25 8:37 */ @Mapper public interface RoleMapper { @Select("SELECT * FROM `role` r WHERE user_id =#{id}") List<Role> findRoleById(Integer id); @Insert("INSERT INTO `role`(role_name,user_id) VALUES(#{roleName},#{userId})") int addRole(Role role); } package com.java.mapper; import com.java.bean.User; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; import java.util.Map; /** * @author yourheart * @Description * @create 2022-06-25 8:10 */ @Mapper public interface UserMapper { @Select("SELECT * FROM `user` WHERE `name`=#{name}") User findByName(@Param("name") String name); @Select("SELECT COUNT(*) FROM `user` WHERE `name`=#{name} AND `password`=#{password}") int selectUserExists(User user); @Insert("INSERT INTO `user`(`name`,`password`) VALUES(#{name},#{password})") int addUser(User user); @Select("SELECT * FROM `user` WHERE id=#{id}") User findById(Integer id); @Select("SELECT a.`name`,a.`password`,b.`role_name`,c.`permission` \n" + "FROM `user` a INNER JOIN `role` b ON a.id=b.user_id INNER JOIN permission c ON b.id=c.role_id") List<Map<String,Object>> getList(); List<User> getListByBean(); }
package com.java.service; import com.java.bean.ResponseBean; import com.java.bean.User; public interface UserService { /** * 通过用户名查询用户的权限 * @param name * @return */ User getUserByName(String name); /** * 添加用户时默认给query权限 * @param user * @return */ ResponseBean addUser(User user); } package com.java.service.impl; import com.java.bean.Permission; import com.java.bean.ResponseBean; import com.java.bean.Role; import com.java.bean.User; import com.java.mapper.PermissionMapper; import com.java.mapper.RoleMapper; import com.java.mapper.UserMapper; import com.java.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * @author yourheart * @Description * @create 2022-06-24 20:34 */ @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private RoleMapper roleMapper; @Autowired private PermissionMapper permissionMapper; /** * 通过用户名查询用户的权限 * * @param name * @return */ @Override public User getUserByName(String name) { User user = userMapper.findByName(name); List<Role> roleByName = roleMapper.findRoleById(user.getId()); roleByName.forEach(role -> { List<Permission> permissionList = permissionMapper.findPermission(role.getId()); role.setPermissions(permissionList); }); user.setRoles(roleByName); return user; } /** * 添加用户时默认给query权限 * * @param user * @return */ @Override public ResponseBean addUser(User user) { ResponseBean responseBean=new ResponseBean(); int selectUserExists = userMapper.selectUserExists(user); if (selectUserExists==1){ responseBean.setCode("-100"); responseBean.setMsg("用户已经存在"); return responseBean; } //入参需要用户名和密码 int addUser = userMapper.addUser(user); //通过用户名查询返回用户的id,向角色表中存放用户的角色和用户的id User byName = userMapper.findByName(user.getName()); Role role=new Role(); role.setRoleName("common"); role.setUserId(byName.getId()); int addRole = roleMapper.addRole(role); //查询角色的id,向权限表存放角色的权限和角色id List<Role> roleById = roleMapper.findRoleById(byName.getId()); roleById.forEach(r->{ Integer rId = r.getId(); List<Permission> permissions=new ArrayList<>(); Permission permission=new Permission(); permission.setPermission("query"); permission.setRoleId(rId); permissions.add(permission); permissionMapper.addBatchPermission(permissions); }); responseBean.setCode("100"); responseBean.setMsg("用户添加成功"); return responseBean; } }
shiro权限控制部分
package com.java.config; import com.java.bean.Permission; import com.java.bean.Role; import com.java.bean.User; import com.java.service.UserService; import lombok.extern.slf4j.Slf4j; 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.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; /** * @author yourheart * @Description 实现AuthorizingRealm接口用户用户认证 * @create 2022-06-15 1:56 */ @Slf4j public class MyShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 角色权限和对应权限添加 * * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取登录用户名 String name = (String) principalCollection.getPrimaryPrincipal(); log.info("【角色权限和对应权限添加】name:{}", name); //查询用户名称 User user = userService.getUserByName(name); log.info("【角色权限和对应权限添加】user:{}", user); //添加角色和权限 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (Role role : user.getRoles()) { //添加角色 simpleAuthorizationInfo.addRole(role.getRoleName()); for (Permission permission : role.getPermissions()) { //添加权限 simpleAuthorizationInfo.addStringPermission(permission.getPermission()); } } return simpleAuthorizationInfo; } /** * 用户认证 * * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //加这一步的目的是在Post请求的时候会先进认证,然后在到请求 log.info("【用户认证】authenticationToken.getPrincipal():{}", authenticationToken.getPrincipal()); if (authenticationToken.getPrincipal() == null) { return null; } //获取用户信息 String name = authenticationToken.getPrincipal().toString(); log.info("【用户认证】name:{}", name); User user = userService.getUserByName(name); log.info("【用户认证】user:{}", user); if (user == null) { //这里返回后会报出对应异常 return null; } else { //这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName()); return simpleAuthenticationInfo; } } } package com.java.config; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; /** * @author yourheart * @Description * @create 2022-06-15 1:58 */ @Configuration @Slf4j public class ShiroConfiguration { /** * 将自己的验证方式加入容器 * * @return */ @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); return myShiroRealm; } /** * 权限管理,配置主要是Realm的管理认证 * * @return */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; } /** * Filter工厂,设置对应的过滤条件和跳转条件 * * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> map = new HashMap<String, String>(); //登出 map.put("/logout", "logout"); /** * Shiro内置过滤器,能够实现拦截器相关的拦截器 * 经常使用的过滤器: * anon:无需认证(登陆)能够访问 * authc:必须认证才能够访问 * user:若是使用rememberMe的功能能够直接访问 * perms:该资源必须获得资源权限才能够访问 * role:该资源必须获得角色权限才能够访问 **/ //对所有用户认证 map.put("/**", "authc"); map.put("/re", "anon"); //登录 shiroFilterFactoryBean.setLoginUrl("/login"); //首页 shiroFilterFactoryBean.setSuccessUrl("/index"); //错误页面,认证不通过跳转 shiroFilterFactoryBean.setUnauthorizedUrl("/error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } /** * 加入注解的使用,不加入这个注解不生效 * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
package com.java.controller.front; import com.java.bean.Role; import com.java.bean.User; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author yourheart * @Description * @create 2022-06-15 2:11 */ @RestController @Slf4j public class TestController { @PostMapping("/login") public String login(@RequestBody User user) { if (StringUtils.isEmpty(user.getName()) || StringUtils.isEmpty(user.getPassword())) { return "请输入用户名和密码!"; } //用户认证信息 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken( user.getName(), String.valueOf(user.getPassword()) ); try { //进行验证,这里可以捕获异常,然后返回对应信息 subject.login(usernamePasswordToken); } catch (UnknownAccountException e) { log.error("用户名不存在!", e); return "用户名不存在!"; } catch (AuthenticationException e) { log.error("账号或密码错误!", e); return "账号或密码错误!"; } catch (AuthorizationException e) { log.error("没有权限!", e); return "没有权限"; } return "login success"; } /** * 用户登出 * @return */ @RequestMapping("/loginOut") public String loginOut(){ Subject subject = SecurityUtils.getSubject(); subject.logout(); return "退出成功"; } @RequiresPermissions("query") @RequestMapping(value = "/index") @ResponseBody public String index(){ return "进入主页"; } @RequiresPermissions("create") @GetMapping("/add") public String add() { return "add success!"; } @RequestMapping(value = "/re") @ResponseBody public String re(){ return "进入注册页面"; } @RequestMapping(value = "/log") @ResponseBody public String log(){ return "进入日志页面"; } }
附上测试地址
http://127.0.0.1:8001/login { "name":"qiuxie", "password":"123" } http://127.0.0.1:8001/index http://127.0.0.1:8001/add 用户登出 http://127.0.0.1:8001/loginOut