
一、说明二、代码实现三、基本组件完整架构实现四、zuul+Shiro实现
4.1、Shiro基本集成4.2、Shiro基本集成---身份验证4.3、Shiro基本集成---权限验证
一、说明Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分。
引用jar包
org.springframework.cloud spring-cloud-starter-netflix-zuul
application.properties配置
server.port=8081
spring.application.name=zuul
eureka.client.service-url.defaultZone= http://localhost:8083/eureka/
#zuul
#以eurekaClient2开头的请求都转发到eurekaClient2
zuul.routes.api-a.path=/eurekaClient1
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() { //数字越小表示顺序越高,越先执行
return 0;
}
@Override
public boolean shouldFilter() {
return true; //表示是否需要执行该filter,true表示执行,false表示不执行
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// System.out.println(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
String accessToken = request.getParameter("token"); // 获取请求的参数
if(StringUtils.isNotBlank(accessToken)) {
//进行路由
ctx.setSendZuulResponse(true);
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);
}else{
//不进行路由
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(400);
ctx.setResponseBody("token is empty");
ctx.set("isSuccess", false);
}
return null;
}
}
没带token参数时返回
带token参数时正常访问后面的系统服务
现在有Eureka Server,端口8083;
有Zuul【zuul】,端口8081;
有Eureka Client【EurekaClient2】,开启分布式,端口分别是7081、7082;
有Eureka Client【EurekaClient1】,端口分别是7080;
现在有EurekaClient2服务可以直接访问接口:http://localhost:7081/getPersonById.do?id=1
现在有EurekaClient1服务可以直接访问接口【这个接口会通过ribbon负载均衡调用EurekaClient2服务----ribbon的实现】:
http://localhost:7080/ribbonPersonById.do
现在通过zuul服务来路由转发:
http://localhost:8081/eurekaClient1/ribbonPersonById.do
http://localhost:8081/EurekaClient2/getPersonById.do?id=1
综合结论:
从上面可以看出,单独的Eureka Client也是可以进行访问的,另外Eureka Client之间可以通过ribbon实现调用。
当有zuul组件时,任何请求都是通过zuul组件所在服务系统进行路由转发到对应Eureka Client上。
因此,上面的请求也反映了zuul组件的功能。
从上面的简单实例,一个完整的系统平台,相关请求都是经过zuul来进行转发路由,因此在实际中“登录鉴权管理”也应该在zuul服务系统上进行构建,这里就介绍zuul+Shiro实现。
4.1、Shiro基本集成引用jar包
org.apache.shiro shiro-spring1.4.0
Shiro的config配置类
@Configuration
public class ShiroConfig {
@Bean
public MyShiroRealm customRealm() {
//用户数据和Shiro数据交互的桥梁。比如需要用户身份认证、权限认证。都是需要通过Realm来读取数据。
MyShiroRealm customRealm = new MyShiroRealm();
return customRealm;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
defaultSecurityManager.setRealm(customRealm());
return defaultSecurityManager;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new MyShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
factoryBean.setLoginUrl("/login"); //这是当任何url时,都进入/login界面
// 登录成功后要跳转的连接
factoryBean.setSuccessUrl("/index"); //当验证成功后,会进入/index
//未授权界面、权限不足跳转页面,跳转到403
factoryBean.setUnauthorizedUrl("/403"); //对应MyShiroRealm类的返回null时,自动跳转到/403
//加载ShiroFilter权限控制规则
loadShiroFilterChain(factoryBean);
return factoryBean;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
private void loadShiroFilterChain(ShiroFilterFactoryBean factoryBean) {
Map filterChainMap = new linkedHashMap();
filterChainMap.put("/login", "anon");
// filterChainMap.put("/register", "anon");
//配置退出 过滤器
filterChainMap.put("/logout", "logout");
//剩余请求需要身份认证
filterChainMap.put("
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//查询mysql获取用户信息 通过浏览器提交的用户名去数据库查找相应的用户信息
UserPo userInfo = userInfoService.selectUser(token.getUsername());
if(userInfo != null){
SecurityUtils.getSubject().getSession().setAttribute("user", userInfo);
// 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验
String userloginName= userInfo.getUserName();
String userPasswd=userInfo.getPassWord();
String name=getName(); //realm name
return new SimpleAuthenticationInfo(userloginName, userPasswd, name);
}
return null;
}
}
------从上面代码看出,通过传入的用户名称去查DB,然后和传来的登录密码对比,相同就说明身份验证通过。
模拟测试
@Controller
public class ShiroController {
// @Autowired
// private SessionDAO sessionDao;
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginForm(Model model) {
model.addAttribute("user", new UserPo());
return "login";
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(UserPo user) {
String username = user.getUserName();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassWord(), false);
// 获取当前的Subject, 代表当前正在执行 *** 作的用户
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(token);
System.out.println("对用户[" + username + "]进行登录验证..验证通过");
} catch (UnknownAccountException uae) {
System.out.println("对用户[" + username + "]进行登录验证..验证未通过,未知账户");
} catch (IncorrectCredentialsException ice) {
System.out.println("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
} catch (LockedAccountException lae) {
System.out.println("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
} catch (ExcessiveAttemptsException eae) {
System.out.println("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");
} catch (AuthenticationException ae) {
// 通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
System.out.println("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");
ae.printStackTrace();
}
// 验证是否登录成功
if (currentUser.isAuthenticated()) {
System.out.println("用户[" + username + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化 *** 作)");
UserPo user2 = (UserPo) SecurityUtils.getSubject().getSession().getAttribute("user");
return "index";
} else {
token.clear();
return "login";
}
}
首先,任意没登录请求来访问都是跳转到登录页面
http://localhost:8081/login
登录成功后,跳转到成功页面。那么,后面直接访问zuul服务的接口都正常访问。
这里zuul主要是做路由转发的,那么事先没登录的访问去访问EurekaClient1,也是先跳转Zuul服务的登录系统,然后接口才能访问。
前面已经说明,当符合身份登录的继续访问,但是不同用户身份的权限又不一样,可以通过不同权限来解决请求是否访问成功。
首先前面Shiro配置类有lifecycleBeanPostProcessor()方法是开启“Shiro权限注解”,那么可以通过注解方法来确定不同类或方法的权限。
还是MyShiroRealm 类增加如下配置内容。
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
UserInfoServiceImpl userInfoService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String loginName = (String) super.getAvailablePrincipal(principalCollection);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//查询mysql获取用户信息
UserPo userInfo = userInfoService.selectUser(loginName);
if(userInfo != null){
info.setStringPermissions(userInfoService.userPerm(loginName));//用户对应的所有权限
SecurityUtils.getSubject().getSession().setAttribute("user", userInfo);
}
return info;
}
分析:userInfoService.userPerm(loginName)方法就是查数据库来获取这个登录用户所赋给的权限。
模拟测试,假设给用户设置user:show、user:admin两个权限,那么
@Override public SetuserPerm(String userName) { Set permSet = new HashSet<>(); //配置的权限,模拟查库 permSet.add("user:show"); permSet.add("user:admin"); return permSet; }
为两个PersonController类、HelloWorldController类配置权限
@Controller
@RequestMapping("/basic")
@RequiresPermissions("user:admin") //这个类下面的方法拥有的权限
public class HelloWorldController {
//http://localhost:8084/basic/sayHello.do
@RequestMapping("/sayHello.do")
@ResponseBody //这是返回json格式
public Object sayHello() {
return "Hello,World!";
}
@RequestMapping("/wrong.html")
public Object wrongPage(){
return "wrong";
}
}
@Controller
@RequestMapping("/person")
@RequiresPermissions("user:person")
public class PersonController {
@Autowired
private PersonServiceImpl personServiceImpl;
//http://localhost:8084/person/getPersonById.do
@RequestMapping("/getPersonById.do")
@ResponseBody
public Object getPersonById(){
PersonPo person = personServiceImpl.getPersonById(1);
return JSON.toJSONString(person);
}
}
直接请求的效果
以上就基本完成Zuul整合Shiro的功能。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)