目录
方法调用逻辑
- shiro config
- subject.login-securityManager.setRealm(myReal);-new MyRealm();
- 回调doGetAuthorizationInfo,返回doGetAuthorizationInfo
- 将dbUser(用户信息)放入session,同时sessionId自动生成。shiro会帮忙维护一个session,与tomcat的session不同,是独立的一套机制,仿照tomcat的那种,与tomcat容器没有关系。java的工程也可以用shiro做会话管理,与web容器没有关系,是独立的。
前面几步都是做认证,最后一步是做会话管理。
Spring的原理
- tomcat启动的时候,有一个监听器listener – 上下文监听器servletContextListener
- spring容器何时启动的呢?就是这个上下文监听器监听到创建上下文,就开始启动spring
- 启动了spring,接下来要做的事情就是spring初始化,加载配置文件,只不过现在把配置文件淘汰掉了,变成配置类了
shiro认证
Shiro 中的 SecurityUtils(转),可参阅:
https://www.cnblogs.com/muxi0407/p/11987661.html
Subject subject = SecurityUtils.getSubject();
subject.login(token);
String sessionId = (String)subject.getSession().getId();
SecurityUtils.getSubject().logout();
SimpleAuthenticationInfo(dbUser,dbUser.getPassword(),getName());
MyRealm的代码
在com.one.shiro下创建类MyRealm.java
package com.one.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.one.pojo.User;
import com.one.service.UserService;
public class MyRealm extends AuthorizingRealm{
@Autowired
UserService userService;
//授权(权限管理)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
// TODO Auto-generated method stub
return null;
}
//身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 认证逻辑
String username = token.getPrincipal().toString();
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
User dbUser = userService.getOne(queryWrapper);
if(dbUser!=null){
/*if(dbUser.getPassword().equals(token.getCredentials())){
}*/
return new SimpleAuthenticationInfo(dbUser,dbUser.getPassword(),getName());
}
return null;
}
}
ShiroConfig.java的代码
package com.one.shiro;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**shiro的配置
* @author chenxia
*
*/
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/user/find", "anon");
filterChainDefinitionMap.put("/user/register", "anon");
// 过滤链定义,从上向下顺序执行
// authc:url都必须认证通过才可以访问; anon:url都可以匿名访问
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// 如果不设置默认会自动寻找Web工程根目录下的"/login"页面
shiroFilterFactoryBean.setLoginUrl("/user/unauth");
return shiroFilterFactoryBean;
}
// 重新设置SecurityManager,通过自动以的MyRealm完成登录校验:
@Bean
public MyRealm myReal() {
return new MyRealm();
}
@Bean
public SecurityManager securityManager(MyRealm myReal) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//自定义session管理
// securityManager.setSessionManager(sessionManager());
// 设置realm
securityManager.setRealm(myReal);
return securityManager;
}
/*@Bean
public SessionManager sessionManager(){
CustomSessionManager manager = new CustomSessionManager();
manager.setSessionDAO(new EnterpriseCacheSessionDAO());
return manager;
}*/
}
登录方法的编码
对com.one.controller中
UserController.java进行修改
//登录
@PostMapping("/login")
@ApiOperation(value="登录方法", httpMethod="POST")
public Result login(User user){
Result result = null;
try {
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
//shiro
Subject subject = SecurityUtils.getSubject();
subject.login(token);
result = new Result("1","登录成功");
} catch (AuthenticationException e) {
if(e instanceof UnknownAccountException){
result = new Result("0","用户名错误");
}else{
result = new Result("0","密码错误");
}
e.printStackTrace();
}
return result;
}
加入sessionId
对com.one.controller中
UserController.java进行修改,加入sessionId相关代码
//将sessionId返回去
String sessionId = (String) SecurityUtils.getSubject().getSession().getId();
result = new Result("1",sessionId,"登录成功");
Result.java的编码
对com.one.common中
Result.java进行修改,新增构造方法
右键->Source->Generate Constructor using Fields…
同时要传3个参数
后端服务器写回的Cookie:
前端服务器的域名,在前端写到浏览器的Cookie里面。
因为域名不同,隔了一层,第二次请求时,自行携带sessionId,是前端服务器发异步请求的时候带过去。
带sessionId有两种方式,一种是请求头header里面带过去,一种是请求体body里面带过去。一般放在请求头header里面带过去。
前端login.html的代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>接口测试平台</title>
<link rel="stylesheet" type="text/css" href="/lemon/css/style.css" />
<style>
body {
height: 100%;
background: #16a085;
overflow: hidden;
}
canvas {
z-index: -1;
position: absolute;
}
.tips{ font-size:12px; height:30px; clear:both;color:red}
.tips_dd_init{
display:none;
}
</style>
<script src="/lemon/js/jquery-1.11.3.js"></script>
<script src="/lemon/js/Particleground.js"></script>
<script type="text/javascript" src="/lemon/js/jquery.md5.js" charset="UTF-8"></script>
<script type="text/javascript" src="/lemon/js/common.js" charset="UTF-8"></script>
<script type="text/javascript" src="/lemon/js/jquery.cookie.js" charset="UTF-8"></script>
<script>
$(function() {
//粒子背景特效
$('body').particleground({
dotColor : '#5cbdaa',
lineColor : '#5cbdaa'
});
$("body").keydown(function(event) {
if (event.keyCode == "13") {//keyCode=13是回车键
$('#login').trigger("click");
}
});
$("#login").click(
function() {
var un = $("[name='username']").val();
var pd = $("[name='password']").val();
if(un==null||un==""){
$(".tips_dd_init").show();
$("p.tips").html("用户名不能为空。");
return false;
}
if(pd==null||pd==""){
$(".tips_dd_init").show();
$("p.tips").html("密码不能为空。");
return false;
}
$(".tips_dd_init").hide();
$.post(lemon.config.global.adminUrl + "/user/login", {
username : un,
password : $.md5(pd)
}, function(ret) {
if (ret != null && ret.status == "1") {
var date = new Date();
date.setTime(date.getTime() + 24 * 60 * 60
* 1000);//1天
//将后端的sessionId写入cookie
$.cookie("sessionId", ret.data, {
expires : date
});
window.location.href = lemon.config.global.rootUrl
+ "/html/projectList.html";
} else {
$(".tips_dd_init").show();
$("p.tips").html(ret.message);
}
}, 'json');
});
});
</script>
</head>
<body>
<form name='loginForm'>
<dl class="admin_login">
<dt>
<strong>接口测试平台</strong> <em>Management System</em>
</dt>
<dd class="user_icon">
<input type="text" placeholder="账号" class="login_txtbx"
name="username" />
</dd>
<dd class="pwd_icon">
<input type="password" placeholder="密码" class="login_txtbx"
name="password" />
</dd>
<dd class="tips_dd_init">
<p class="tips" style="color:red"></p>
</dd>
<dd>
<input type="button" value="立即登录" class="submit_btn" id="login"/>
</dd>
<dd>
<p>
还没账号?<a href="register.html" style="text-decoration: underline;"><strong>去注册</strong></a>
</p>
</dd>
</dl>
</form>
</body>
</html>