SpringBoot整合Shiro实现登录认证,鉴权授权

文章目录

  • 前言
  • 一、shiro简介
  • 二、环境搭建
    • 2.1.数据库
      • 2.1.1user用户表
      • 2.1.2user_role用户角色关系表
      • 2.1.3role角色表
      • 2.1.4role_permission角色权限关系表
      • 2.1.5permission权限表
    • 2.2导坐标
    • 2.3实体类
      • 2.3.1User
      • 2.3.2Role
      • 2.3.3Permission
    • 2.4MVC三层
      • 2.4.1User
        • 2.4.1.1mapper层
        • 2.4.1.2service层
        • 2.4.1.3controller层
      • 2.4.2Role
        • 2.4.2.1mapper层
        • 2.4.2.2service层
        • 2.4.2.3controller层
      • 2.3.3Permission
        • 2.4.3.1mapper层
        • 2.4.3.2service层
        • 2.4.3.3controller层
    • 2.5.加密工具类
    • 2.6.全局异常处理器
    • 2.7自定义Realm
    • 2.8shiro配置类
  • 三、案例演示
    • 3.1 登录认证
    • 3.2 权限认证
  • 总结


前言

近期对Springboot框架的学习中,为了更好的学习理解Springsecurity中间件,先学习了一下“老派”的shiro安全框架,本文章将通过注解的方式实现基础的用户认证和角色授权案例

一、shiro简介

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

二、环境搭建

2.1.数据库

2.1.1user用户表

SpringBoot整合Shiro实现登录认证,鉴权授权_第1张图片

2.1.2user_role用户角色关系表

SpringBoot整合Shiro实现登录认证,鉴权授权_第2张图片

2.1.3role角色表

SpringBoot整合Shiro实现登录认证,鉴权授权_第3张图片

2.1.4role_permission角色权限关系表

SpringBoot整合Shiro实现登录认证,鉴权授权_第4张图片

2.1.5permission权限表

SpringBoot整合Shiro实现登录认证,鉴权授权_第5张图片

2.2导坐标

<properties>
        <java.version>1.8</java.version>
        <!--shiro-->
        <shiro.version>1.3.2</shiro.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

        <!-- SECURITY begin -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- SECURITY end -->
    </dependencies>

2.3实体类

2.3.1User

@TableName("pe_user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(value = "id",type = IdType.AUTO)
    private int id;
    private String username;
    private String password;
    private String salt;
    @TableField(exist = false)
    private Set<Role> roles;
}

2.3.2Role

@TableName("pe_role")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
    @TableId(value = "id",type = IdType.AUTO)
    private int id;
    private String name;
    private String code;
    private String description;
    @TableField(exist = false)
    private Set<Permission> permissions;
}

2.3.3Permission

@TableName("pe_permission")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {
    @TableId(value = "id",type = IdType.AUTO)
    private int id;
    private String name;
    private String code;
    private String description;
}

2.4MVC三层

2.4.1User

2.4.1.1mapper层

public interface UserMapper extends BaseMapper<User> {

    @Select("select * from pe_user where username = #{name}")
    User findUserByName(String name);
		//级联查询
    @Results({
            @Result(column = "id",property = "id"),
            @Result(column = "username",property = "username"),
            @Result(column = "password",property = "password"),
            @Result(column = "salt",property = "salt"),
            @Result(column = "id",property = "roles",
                    many=@Many(select = "com.apesource.shirostudy_demo_05.dao.RoleMapper.findRoleIdsByUserId"))

    })
    @Select("select * from pe_user where id = #{id}")
    User findUserDetailById(@Param("id") int id);
}

2.4.1.2service层

public interface IUserService extends IService<User> {
    User findUserByName(String name);

    User findUserDetailById(int id);
}

@Service
@SuppressWarnings("all")
public class UserServiceImp extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    IRoleService roleService;

    @Override
    public User findUserByName(String name) {
        User user = userMapper.findUserByName(name);
        return user;
    }

    @Override
    public User findUserDetailById(int id) {
        //根据userid查询user
        User user = userMapper.findUserDetailById(id);
        //返回
        return user;
    }
}

2.4.1.3controller层

@RestController
public class UserController {
    @Autowired
    private IUserService userService;

    //个人主页
    @RequiresPermissions("user-home")
    @RequestMapping(value = "/user/home")
    public String home() {
        return "访问个人主页成功";
    }

    //根据名字查询用户基本信息
    @RequiresPermissions("user-find")
    @RequestMapping(value = "/user/userByName/{name}")
    public String findUserByName(@PathVariable String name) {
        System.out.println(name);
        User user = userService.findUserByName(name);
        return user.toString();
    }


    //更新
    @RequiresPermissions("user-update")
    @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
    public String update(@PathVariable String id) {
        return "更新用户成功";
    }


    //删除
    @RequiresPermissions("user-delete")
    @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable String id) {
        return "删除用户成功";
    }


    //根据id查询用户详细信息
    @RequiresPermissions("user-find")
    @RequestMapping(value = "/user/userById/{id}")
    public String findUserDetailById(@PathVariable int id) {
        return userService.findUserDetailById(id).toString();
    }

    //登录页面
    @RequestMapping(value = "/login")
    public String login(User user) {
        try {
            //1.构造登录令牌
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
            //2.//2.获取subject
            Subject subject = SecurityUtils.getSubject();
            //3.调用subject进行登录
            subject.login(token);
            return "登录成功!!!";
        } catch (AuthenticationException e) {
            return "用户名或密码错误!!!";
        }


    }

    //未登陆与未授权页面
    @RequestMapping(value = "/autherror")
    public String autherror() {
        return "未登录";
    }

}

2.4.2Role

2.4.2.1mapper层

public interface RoleMapper extends BaseMapper<Role> {
    @Results({
            @Result(column = "id",property = "id"),
            @Result(column = "name",property = "name"),
            @Result(column = "code",property = "code"),
            @Result(column = "description",property = "description"),
            @Result(column = "id",property = "permissions",
                    many =@Many(select = "com.apesource.shirostudy_demo_05.dao.PermissionMapper.findPermissionIdsByRoleId"))
    })
    @Select("SELECT * FROM pe_role WHERE id IN (SELECT role_id FROM pe_user_role WHERE user_id = #{id})")
    Set<Role> findRoleIdsByUserId(int id);
}

2.4.2.2service层

public interface IRoleService extends IService<Role> {
    Set<Role> findRoleIdsByUserId(int id);
}
@Service
@SuppressWarnings("all")
public class RoleServiceImp extends ServiceImpl<RoleMapper, Role> implements IRoleService {
    @Autowired
    RoleMapper roleMapper;
    @Autowired
    IPermissionService permissionService;

    @Override
    public Set<Role> findRoleIdsByUserId(int id) {
        Set<Role> roles = roleMapper.findRoleIdsByUserId(id);
        return roles;
    }
}

2.4.2.3controller层

由于不演示,无。

2.3.3Permission

2.4.3.1mapper层

public interface PermissionMapper extends BaseMapper<Permission> {


    @Results({
            @Result(column = "id",property = "id"),
            @Result(column = "name",property = "name"),
            @Result(column = "code",property = "code"),
            @Result(column = "description",property = "description"),
    })
    @Select("SELECT * FROM pe_permission WHERE id IN (SELECT permission_id FROM pe_role_permission WHERE role_id = #{id} ) ")
    Set<Permission> findPermissionIdsByRoleId(int id);
}

2.4.3.2service层

public interface IPermissionService extends IService<Permission> {
    Set<Permission> findPermissionIdsByRoleId(int id);
}
@Service
public class PermissionServiceImp extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {
    @Autowired(required = false)
    PermissionMapper permissionMapper;

    @Override
    public Set<Permission> findPermissionIdsByRoleId(int id) {
        Set<Permission> permissionIds = permissionMapper.findPermissionIdsByRoleId(id);
        return permissionIds;
    }
}

2.4.3.3controller层

由于不演示,无。

2.5.加密工具类

public class DigestsUtil {

    public static final String SHA1 = "SHA-1";

    public static final Integer COUNTS =369;

    /**
     * @Description sha1方法
     * @param input 需要散列字符串
     * @param salt 盐字符串
     * @return
     */
    public static String show(String input, String salt) {
       return new SimpleHash(SHA1, input, salt,COUNTS).toString();
    }

    /**
     * @Description 随机获得salt字符串
     * @return
     */
    public static String generateSalt(){
        SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
        return randomNumberGenerator.nextBytes().toHex();
    }


    /**
     * @Description 生成密码字符密文和salt密文
     * @param
     * @return
     */
    public static Map<String,String> entryptPassword(String passwordPlain) {
       Map<String,String> map = new HashMap<>();
       String salt = generateSalt();
       String password =show(passwordPlain,salt);
       map.put("salt", salt);
       map.put("password", password);
       return map;
    }
}

2.6.全局异常处理器

@ControllerAdvice
public class BaseExceptionHandler {
    @ExceptionHandler(value = AuthorizationException.class)
    @ResponseBody
    public String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e){
        return "未授权!!!";
    }
}

2.7自定义Realm

public class MyRealm extends AuthorizingRealm {
    @Autowired
    IUserService userService;



    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        int userId = (int) principalCollection.getPrimaryPrincipal();
        User user = userService.findUserDetailById(userId);
        Set<String> roles = new HashSet<>();
        Set<String> permissions = new HashSet<>();
        user.getRoles().stream().forEach(role -> {
            roles.add(role.getCode());
            role.getPermissions().stream().forEach(permission -> {
                permissions.add(permission.getCode());
            });
        });
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }

    /**
     * 认证方法
     *  参数:传递的用户名密码
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.获取登录的用户名密码(token)
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        //2.根据用户名查询数据库
        //mybatis情景下:user对象中包含ID,name,pwd(匿名)
        User user = userService.findUserByName(username);
        if(user != null){
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),"myRealm");
            return info;
        }
        return null;
    }


    /**
     * @Description 自定义密码比较器
     * @param
     * @return
     * bean标签 init-method属性
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        //指定密码算法
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
        //指定迭代次数
        hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);
        //生效密码比较器
        setCredentialsMatcher(hashedCredentialsMatcher);
    }
}

2.8shiro配置类

@Configuration
public class ShiroConfiguration {

    /**
     * 1.创建shiro自带cookie对象
     */
    @Bean
    public SimpleCookie sessionIdCookie(){
        SimpleCookie simpleCookie = new SimpleCookie();
        simpleCookie.setName("ShiroSession");
        return simpleCookie;
    }

    //2.创建realm
    @Bean
    public MyRealm getRealm(){
        return new MyRealm();
    }

    /**
     * 3.创建会话管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setGlobalSessionTimeout(3600000);
        return sessionManager;
    }

    //4.创建安全管理器
    @Bean
    public SecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }



    /**
     * 5.保证实现了Shiro内部lifecycle函数的bean执行
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 6.开启对shiro注解的支持
     *   AOP式方法级权限检查
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }




    //8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        //1.创建过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        //2.设置安全管理器
        filterFactory.setSecurityManager(defaultWebSecurityManager());
        //3.通用配置(跳转登录页面,为授权跳转的页面)
        filterFactory.setLoginUrl("/autherror");//跳转url地址
        //4.设置过滤器集合
        //key = 拦截的url地址
        //value = 过滤器类型
        Map<String,String> filterMap = new LinkedHashMap<>();
        //key:请求规则   value:过滤器名称
        filterMap.put("/login","anon");//当前请求地址可以匿名访问
        filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问
        //在过滤器工程内设置系统过滤器
        filterFactory.setFilterChainDefinitionMap(filterMap);

        return filterFactory;
    }
    
}

三、案例演示

用户密码均是123456加密后保存至数据库

3.1 登录认证

成功登录:
SpringBoot整合Shiro实现登录认证,鉴权授权_第6张图片
失败登录:
SpringBoot整合Shiro实现登录认证,鉴权授权_第7张图片

3.2 权限认证

由于刚刚登录的信息有管理员和员工的身份,所以其所有功能都能使用。
有权限时:SpringBoot整合Shiro实现登录认证,鉴权授权_第8张图片
无权限时:SpringBoot整合Shiro实现登录认证,鉴权授权_第9张图片
无权限时会被全局异常处理器拦截,并作出响应的响应。


总结

通过对shrio的学习,更好的理解了安全认证以及鉴权授权的流程,大概了解了安全认证的机制,对我之后学习理解Springsecurity有更好的帮助。

你可能感兴趣的:(spring,boot,后端,java)