Shiro+Mybatis实现登录认证、授权功能
Shiro+Mybatis实现登录认证、授权功能
一、实现登录认证功能
-
1、流程:
- 跟据用户提交表单的账号,经Mybatis框架在数据库中查出User对象:
- 如果User为空,则会抛出异常:UnknownAccountException,没有此账户名。
- 如果不为空,则比对表单中的密码和User对象的密码是否相同(shiro框架自动完成,有加密),密码不相同则会抛出异常:IncorrectCredentialsException,密码错误;密码相同,则登陆成功。
- 跟据用户提交表单的账号,经Mybatis框架在数据库中查出User对象:
-
2、详细流程:
-
1)代码结构如图:
-
2)RouterController类接收表单的请求后,将用户提交的表单数据封装成令牌,并执行方法 subjec.login(token)(用令牌登陆),下面是RouterController的部分代码
@RequestMapping("/login") public String login(String usr, String pwd, Model model){ //获取当前用户 Subject subject = SecurityUtils.getSubject(); //封装用户的登陆数据,生成令牌 UsernamePasswordToken token = new UsernamePasswordToken(usr,pwd); //用令牌登陆,如果没有异常则登陆成功 try{ subject.login(token); //无异常则登陆成功 return "index"; }catch(UnknownAccountException e){ model.addAttribute("msg","用户名错误"); return "login"; }catch(IncorrectCredentialsException e){ model.addAttribute("msg","密码错误"); return "login"; } }
-
3)subjec.login(token)会调用UserRealm的认证方法doGetAuthenticationInfo(AuthenticationToken aToken)(UserRealm中有两个方法,一个授权,一个认证),下面是UserRealm的部分代码(认证方法):
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //先取令牌 UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; //根据令牌信息从数据库中取出用户 User user = userService.getUserByEmail(token.getUsername()); if(user==null){ return null; //返回null,则抛出无用户名的异常 } //验证密码,Shiro自动验证,只需要把数据库的密码传过去就好了 return new SimpleAuthenticationInfo(user,user.getPassword(),""); }
-
二、实现授权功能
-
1、给请求设置权限,下面是ShiroConfig三病(Bean)的第一个病(Bean)ShiroFilterFactoryBean,主要负责给各种请求设置各种权限,只有拥有权限的用户才可访问请求
@Bean //此注解意思就是在程序开始运行前,会自动给spring托管 public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //关联securityManager bean.setSecurityManager(securityManager); //给请求设置权限 Map<String,String> filter = new LinkedHashMap<>(); filter.put("/user/information","perms[user:gr]"); //有权限"user:gr"才可访问 filter.put("/user/recommend","perms[user:tj]"); //有权限"user:tj"才可访问 filter.put("/","anon"); //anon 谁都可以访问 //把filter加载给bean bean.setFilterChainDefinitionMap(filter); //当没有登陆时,跳转到此登陆界面 bean.setLoginUrl("/tologin"); //当没有权限时,跳转到此登陆界面 bean.setUnauthorizedUrl("/noautho"); return bean; }
-
2、根据用户对象的perm(数据库中代表权限的字段)赋予当前用户权限,下面是UserRealm类中的授权方法:doGetAuthorizationInfo(PrincipalCollection principalCollection)
//授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //拿到当前登陆的这个对象,根据这个对象的perm属性给其授权 Subject subject = SecurityUtils.getSubject(); User currentUser = (User)subject.getPrincipal(); //授权 info.addStringPermission(currentUser.getPerm()); return info; }
三、涉及到的主要代码
ShiroConfig.java
- 有三病(Bean),分别代表Shiro三核心,Bean1负责给请求设置权限
package com.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig{
//shriofilterbean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//关联securityManager
bean.setSecurityManager(securityManager);
//给请求设置权限
Map<String,String> filter = new LinkedHashMap<>();
filter.put("/user/information","perms[user:gr]");
filter.put("/user/recommend","perms[user:tj]");
filter.put("/","anon");
bean.setFilterChainDefinitionMap(filter);
//当没有登陆时,跳转到此登陆界面
bean.setLoginUrl("/tologin");
//当没有权限时,跳转到此登陆界面
bean.setUnauthorizedUrl("/noautho");
return bean;
}
//securityManager
@Bean
public DefaultWebSecurityManager securityManager(@Qualifier("realm") UserRealm realm){
System.out.println("@securityManager");
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(realm);
return securityManager;
}
//realm
@Bean
public UserRealm realm(){
System.out.println("@realm");
return new UserRealm();
}
}
UserRealm.java
- 两方法,一给用户授权,一给登陆认证
package com.config;
import com.pojo.User;
import com.service.UserService;
import org.apache.shiro.SecurityUtils;
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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
//自定义的realm
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("@授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登陆的这个对象,根据这个对象的perm属性给其授权
Subject subject = SecurityUtils.getSubject();
User currentUser = (User)subject.getPrincipal();
//授权
info.addStringPermission(currentUser.getPerm());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//先取令牌
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//根据令牌信息从数据库中取出用户
User user = userService.getUserByEmail(token.getUsername());
if(user==null){
return null; //返回null,则抛出无用户名的异常
}
//验证密码,Shiro自动验证,只需要把数据库的密码传过去就好了
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
RouterController.java
- 负责接收请求,所有的请求接口都在这
package com.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class RouterController {
@RequestMapping({"/","/index"})
public String toWelcome(){
return "index";
}
@RequestMapping("/user/information")
public String toInformation(){
return "user/information";
}
@RequestMapping("/user/recommend")
public String toRecommend(){
return "user/recommend";
}
@RequestMapping("/tologin")
public String toLogin(){
return "login";
}
@RequestMapping("/login")
public String login(String usr, String pwd, Model model){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登陆数据,生成令牌
UsernamePasswordToken token = new UsernamePasswordToken(usr,pwd);
//用令牌登陆,如果没有异常则登陆成功
try{
subject.login(token);
//无异常则登陆成功
return "index";
}catch(UnknownAccountException e){
model.addAttribute("msg","用户名错误");
return "login";
}catch(IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
}
@RequestMapping("/noautho")
@ResponseBody
public String toNoautho(){
return "没有权限访问!";
}
}