依赖包
数据库表一切从简,用户 user 表,以及角色 role 表 user role Shiro 相关类Shiro 配置类@Configurationpublic class ShiroConfig { @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的'/login.jsp'页面 或 '/login' 映射 shiroFilterFactoryBean.setLoginUrl('/notLogin'); // 设置无权限时跳转的 url; shiroFilterFactoryBean.setUnauthorizedUrl('/notRole'); // 设置拦截器 Map 注意:里面的 SecurityManager 类导入的应该是 shirFilter 方法中主要是设置了一些重要的跳转 url,比如未登陆时,无权限时的跳转;以及设置了各类 url 的权限拦截,比如 /user 开始的 url 需要 user 权限,/admin 开始的 url 需要 admin 权限等 权限拦截 Filter当运行一个Web应用程序时,Shiro将会创建一些有用的默认 Filter 实例,并自动地将它们置为可用,而这些默认的 Filter 实例是被 DefaultFilter 枚举类定义的,当然我们也可以自定义 Filter 实例,这些在以后的文章中会讲到 DefaultFilter
常用的主要就是 anon,authc,user,roles,perms 等 注意:anon, authc, authcBasic, user 是第一组认证过滤器,perms, port, rest, roles, ssl 是第二组授权过滤器,要通过授权过滤器,就先要完成登陆认证操作(即先要完成认证才能前去寻找授权) 才能走第二组授权器(例如访问需要 roles 权限的 url,如果还没有登陆的话,会直接跳转到 自定义 realm 类我们首先要继承 AuthorizingRealm 类来自定义我们自己的 realm 以进行我们自定义的身份,权限认证操作。 public class CustomRealm extends AuthorizingRealm { private UserMapper userMapper; @Autowired private void setUserMapper(UserMapper userMapper) { this.userMapper = userMapper; } /** * 获取身份验证信息 * Shiro中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。 * * @param authenticationToken 用户身份信息 token * @return 返回封装了用户信息的 AuthenticationInfo 实例 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println('————身份认证方法————'); UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; // 从数据库获取对应用户名密码的用户 String password = userMapper.getPassword(token.getUsername()); if (null == password) { throw new AccountException('用户名不正确'); } else if (!password.equals(new String((char[]) token.getCredentials()))) { throw new AccountException('密码不正确'); } return new SimpleAuthenticationInfo(token.getPrincipal(), password, getName()); } /** * 获取授权信息 * * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println('————权限认证————'); String username = (String) SecurityUtils.getSubject().getPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //获得该用户角色 String role = userMapper.getRole(username); Set 重写的两个方法分别是实现身份认证以及权限认证,shiro 中有个作登陆操作的 其中 doGetAuthorizationInfo 方法只有在需要权限认证时才会进去,比如前面配置类中配置了 再说下 UsernamePasswordToken 类,我们可以从该对象拿到登陆时的用户名和密码(登陆时会使用 token.getUsername() //获得用户名 Stringtoken.getPrincipal() //获得用户名 Object token.getPassword() //获得密码 char[]token.getCredentials() //获得密码 Object 注意:有很多人会发现,UserMapper 等类,接口无法通过 @Autowired 注入进来,跑程序的时候会报 NullPointerException,网上说了很多诸如是 Spring 加载顺序等原因,但其实有一个很重要的地方要大家注意,CustomRealm 这个类是在 shiro 配置类的 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(customRealm()); return securityManager; } @Bean public CustomRealm customRealm() { return new CustomRealm(); } 道理也很简单,和 Controller 中调用 Service 一样,都是 SpringBean,不能自己 new 当然,同样的道理也可以这样写: @Bean public SecurityManager securityManager(CustomRealm customRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(customRealm); return securityManager; } 然后只要在 CustomRealm 类加上个类似 @Component 的注解即可 功能实现本文的功能全部以接口返回 json 数据的方式实现 根据 url 权限分配 controller游客@RestController@RequestMapping('/guest')public class GuestController{ @Autowired private final ResultMap resultMap; @RequestMapping(value = '/enter', method = RequestMethod.GET) public ResultMap login() { return resultMap.success().message('欢迎进入,您的身份是游客'); } @RequestMapping(value = '/getMessage', method = RequestMethod.GET) public ResultMap submitLogin() { return resultMap.success().message('您拥有获得该接口的信息的权限!'); }} 普通登陆用户@RestController@RequestMapping('/user')public class UserController{ @Autowired private final ResultMap resultMap; @RequestMapping(value = '/getMessage', method = RequestMethod.GET) public ResultMap getMessage() { return resultMap.success().message('您拥有用户权限,可以获得该接口的信息!'); }} 管理员@RestController@RequestMapping('/admin')public class AdminController { @Autowired private final ResultMap resultMap; @RequestMapping(value = '/getMessage', method = RequestMethod.GET) public ResultMap getMessage() { return resultMap.success().message('您拥有管理员权限,可以获得该接口的信息!'); }} 突然注意到 CustomRealm 类那里抛出了 AccountException 异常,现在建个类进行异常捕获 @RestControllerAdvicepublic class ExceptionController { private final ResultMap resultMap; @Autowired public ExceptionController(ResultMap resultMap) { this.resultMap = resultMap; } // 捕捉 CustomRealm 抛出的异常 @ExceptionHandler(AccountException.class) public ResultMap handleShiroException(Exception ex) { return resultMap.fail().message(ex.getMessage()); }} 还有进行登陆等处理的 LoginController @RestControllerpublic class LoginController { @Autowired private ResultMap resultMap; private UserMapper userMapper; @RequestMapping(value = '/notLogin', method = RequestMethod.GET) public ResultMap notLogin() { return resultMap.success().message('您尚未登陆!'); } @RequestMapping(value = '/notRole', method = RequestMethod.GET) public ResultMap notRole() { return resultMap.success().message('您没有权限!'); } @RequestMapping(value = '/logout', method = RequestMethod.GET) public ResultMap logout() { Subject subject = SecurityUtils.getSubject(); //注销 subject.logout(); return resultMap.success().message('成功注销!'); } /** * 登陆 * * @param username 用户名 * @param password 密码 */ @RequestMapping(value = '/login', method = RequestMethod.POST) public ResultMap login(String username, String password) { // 从SecurityUtils里边创建一个 subject Subject subject = SecurityUtils.getSubject(); // 在认证提交前准备 token(令牌) UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 执行认证登陆 subject.login(token); //根据权限,指定返回数据 String role = userMapper.getRole(username); if ('user'.equals(role)) { return resultMap.success().message('欢迎登陆'); } if ('admin'.equals(role)) { return resultMap.success().message('欢迎来到管理员页面'); } return resultMap.fail().message('权限错误!'); }} 测试登陆前访问信息接口 普通用户登陆 密码错误 管理员登陆 获取信息 获取信息 注销 |
|
来自: 甘甘灰 > 《springboot整合》