springboot整合shiro框架做用户认证与授权
springboot整合shiro框架的流程主要分为:
- 导入shiro依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.1</version> </dependency>
- 编写shiro配置类,其中shiroconfig类主要用于配置过滤器,管理器和自定义的认证鉴权类。userRealm类是自定义的认证鉴权类继承自AuthorizingRealm类。
package com.codeyoung.shirodemo.config;/** * @author: ouououou * @date: 2021/11/3 14:17 * @description: */ import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @ClassName ShiroConfig * @Description TODO * @Author ouououou * @Date 2021/11/3 14:17 * @Version 1.0 **/ @Configuration public class ShiroConfig { //ShiroFilterFactoryBean DefaultWebSecurityManager Realm @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Autowired DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置管理器 shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); //设置拦截器 Map<String, String> map=new LinkedHashMap<>(); //设置需要拦截的请求和内置的过滤器 anon:无需认证 authc:必须认证了才能通过 user:必须拥有记住我功能 perms:拥有某个资源的权限 role:拥有某个角色权限 map.put("/user/**","authc"); //可以在map中进行权限过滤 添加的过滤器类型为perms 表示拥有user:add权限的用户才可以访问/user/add页面 map.put("/user/add","perms[user:add]"); //设置未授权跳转页面 shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); shiroFilterFactoryBean.setLoginUrl("/toLogin"); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(@Autowired UserRealm userRealm){ DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(userRealm); return defaultWebSecurityManager; } @Bean public UserRealm getUserRealm(){ return new UserRealm(); } }
package com.codeyoung.shirodemo.config;/** * @author: ouououou * @date: 2021/11/3 14:17 * @description: */ import com.sun.crypto.provider.PBEWithMD5AndDESCipher; import org.apache.catalina.User; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.Md5CredentialsMatcher; 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.subject.Subject; /** * @ClassName UserRealm * @Description TODO * @Author ouououou * @Date 2021/11/3 14:17 * @Version 1.0 **/ public class UserRealm extends AuthorizingRealm { /** * @description 授权 用户认证后经过授权过滤会进入此方法,可在此方法中进行授权 * @param principalCollection * @date 2021/11/3 14:25 * @return org.apache.shiro.authz.AuthorizationInfo */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //获取当前登录的用户为其授权 Subject subject = SecurityUtils.getSubject(); //用户经过认证后将其用户信息放入subject的principal中获取出来 //User principal = (User) subject.getPrincipal(); //通过用户的信息查询数据库中的权限为其授权 simpleAuthorizationInfo.addStringPermission(""); return simpleAuthorizationInfo; } /** * @description 认证 控制层调用login方法会进入此方法执行 * @param authenticationToken * @date 2021/11/3 14:25 * @return org.apache.shiro.authc.AuthenticationInfo */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //将token转为保存用户名密码的token UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; //获取用户名 String username = userToken.getUsername(); //查询数据库获取用户信息 //User user=userService.findUserByName(username); //通过用户名查询出对应的密码进行认证 模拟查询回来的密码 String password="132"; Md5CredentialsMatcher md5CredentialsMatcher = new Md5CredentialsMatcher(); return new SimpleAuthenticationInfo("", "password", ""); } }
- 当访问任意页面时会进入ShiroConfig类中的过滤器,验证访问该页面需要的认证要求。如果需要登录则会跳转到登录页面。
- 当前端发起登录请求,通过SecurityUtils获取到当前登录的用户,将用户的数据封装到一个UsernamePasswordToken中,执行subject的login方法会进入到自定义认证鉴权类的认证方法doGetAuthenticationInfo(),将方法参数的token转为配置了用户登录参数的token,取出用户名在数据库查询出用户信息后,通过shiro框架的SimpleAuthenticationInfo对象传入密码,shiro框架会对密码进行验证。
@RequestMapping("/login") public String login(@RequestParam("username") String username,@RequestParam("password") String password,Model model){ //获取当前登录用户 Subject subject = SecurityUtils.getSubject(); //封装用户登录数据 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password); //登录请求会进到认证方法中 try { subject.login(usernamePasswordToken); return "index"; }catch (UnknownAccountException e){ model.addAttribute("msg","用户名错误"); return "login"; }catch (IncorrectCredentialsException e){ model.addAttribute("msg","密码错误"); return "login"; }catch (AuthenticationException e) { e.printStackTrace(); return "login"; } }
- 登陆成功后访问页面,如果页面需要授权则会经过shiroConfig的过滤器跳转到授权的页面,并对用户进行授权。UserRealm类的doGetAuthorizationInfo方法可以对用户进行授权,通过SecurityUtils获取出当前登录的用户,查询数据库获得当前登录的用户的权限列表,再通过simpleAuthorizationInfo对象将用户的权限设置后返回。
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //获取当前登录的用户为其授权 Subject subject = SecurityUtils.getSubject(); //用户经过认证后将其用户信息放入subject的principal中获取出来 //User principal = (User) subject.getPrincipal(); //通过用户的信息查询数据库中的权限为其授权 simpleAuthorizationInfo.addStringPermission(""); simpleAuthorizationInfo.setObjectPermissions(new HashSet<>()); return simpleAuthorizationInfo; }