您的位置:首页 > 编程语言 > Java开发

spring springmvc shiro 实现单点登录demo

2017-04-25 09:27 309 查看
一.数据库建表

CREATE TABLE `demo_member` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`account` varchar(20) NOT NULL DEFAULT '',
`password` varchar(32) NOT NULL DEFAULT '',
`email` varchar(40) NOT NULL DEFAULT '',
`qq` varchar(11) NOT NULL DEFAULT '',
`mobile` varchar(11) NOT NULL DEFAULT '',
`nickname` varchar(20) NOT NULL DEFAULT '',
`realname` varchar(10) NOT NULL DEFAULT '',
`avatar` varchar(200) NOT NULL DEFAULT '',
`gender` smallint(2) NOT NULL DEFAULT '-1' COMMENT '1:M 0:F',
`auth_token` varchar(40) NOT NULL DEFAULT '',
`token_expire` int(11) NOT NULL DEFAULT '0',
`salt` char(6) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

二.mybatis
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="MemberMapper">
<!-- 登录 -->
<select id="memberLogin" parameterType="Map" resultType="Map">
SELECT nickname,avatar,gender,auth_token,token_expire,salt FROM demo_member
<where>
account =#{account} AND password=#{password}
</where>
union
SELECT nickname,avatar,gender,auth_token,token_expire,salt FROM demo_member
<where>
email =#{account} AND password=#{password}
</where>
</select>
<!-- 获取验证token -->
<select id="authToken" parameterType="String" resultType="Map">
SELECT auth_token,token_expire FROM demo_member
<where>
account =#{account}
</where>
union
SELECT auth_token,token_expire FROM demo_member
<where>
email =#{account}
</where>
</select>
<!-- 登录更新 token 相关字段 salt:加密用 token_expire:token过期时间 -->
<update id="updateToken" parameterType="Map">
update demo_member set salt=#{salt},auth_token=#{auth_token},token_expire=#{token_expire} where account =#{account}
</update>
<!-- 每次请求接口 更新token过期时间 -->
<update id="updateTokenExpire" parameterType="Map">update demo_member set token_expire=#{token_expire} where account =#{account}</update></mapper>

三.shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd" default-lazy-init="true">

<!-- ================ Shiro start ================ -->
<!-- 自定义Realm -->
<bean id="ShiroRealm" class="com.demo.interceptors.shiro.ShiroRealm" ></bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="ShiroRealm" />
</bean>
<!-- Shiro Filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 要求登录时的链接(登录页面地址),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="/"></property>
<!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码) -->
<!-- <property name="successUrl" value="/" ></property> -->
<!-- 用户访问未对其授权的资源时,所显示的连接 -->
<property name="unauthorizedUrl" value="/"></property>

<property name="filterChainDefinitions">
<value>
<!-- anon:地址不需要任何权限即可访问 -->
/admin/index = anon
<!-- perms["admin:product"] 需要权限为admin:product的用户-->

<!-- roles["admin"] 需要角色为admin的用户
/admin/index roles["admin"]-->

/index/index = authc
/basic/** = authc
<!-- /admin/ = anon
/admin/index.jsp = anon
/admin/login.jsp = authc
/admin/logout.jsp = logout
/admin/common/captcha.jhtml = anon

/admin/product/** = perms["admin:product"]

/admin/** = authc -->

</value>
</property>
</bean>
<!-- ================ Shiro end ================ -->

</beans>针对以上shiro配置相应的ShiroRealm.java如下
public class ShiroRealm extends AuthorizingRealm{
/**
* 权限认证,获取登录用户的权限 (此处对笔者要实现的功能无关紧要,可忽略)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

return null;
}

/**
* 登录认证,创建用户的登录信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取基于用户名和密码的令牌
//实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
//两个token的引用都是一样的
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
String username = usernamePasswordToken.getUsername();
String password = MD5.md5_1(new String(usernamePasswordToken.getPassword()));
Map<String, Object> params = new HashMap<>();
params.put("account", username);
params.put("password", password);
Map<String, Object> member = null;
try
{
member = memberService.memberLogin(params);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
if(member!=null){
String salt = Salt.salting();
System.out.println("salt:"+salt);
String auth_token = SHA.sha(salt+username,"SHA-1");
System.out.println(auth_token);
long token_expire = System.currentTimeMillis()/1000+10;
System.out.println(token_expire);
params.put("salt", salt);
params.put("auth_token", auth_token);
params.put("token_expire", token_expire);
try
{
memberService.updateToken(params);

}
catch (Exception e)
{
e.printStackTrace();
return null;
}
//后期可以memcached、redis做缓存 存储 以减小服务器压力
this.setSession("currentUser", username);
this.setSession("auth_token", auth_token);
//this.setSession("token_expire", token_expire);
return new SimpleAuthenticationInfo(username, usernamePasswordToken.getPassword(),getName());
}
return null;
}
/**
* 将一些数据放到ShiroSession中,以便于其它地方使用
* @see  比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到
*/
private void setSession(Object key, Object value){
Subject currentUser = SecurityUtils.getSubject();
if(null != currentUser){
Session session = currentUser.getSession();
if(null != session){
session.setAttribute(key, value);
}
}
}
}

四.Salt文件

public class Salt
{
private static final String salt[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };

public static String salting()
{
StringBuffer s = new StringBuffer();
Random r = new Random();
for (int i = 0; i < 6; i++)
{
s.append(salt[r.nextInt(salt.length)]);
}
return s.toString();
}

public static void main(String[] args)
{
salting();
}
}

五.MD5以及SHA 文件
public class MD5 {

public static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte b[] = md.digest();

int i;

StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
str = buf.toString();
} catch (Exception e) {
e.printStackTrace();

}
return str;
}
public static String md5_1(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
//str =  new BigInteger(1, md.digest()).toString(16);
BASE64Encoder base64en = new BASE64Encoder();
str  = base64en.encode(md.digest());
} catch (Exception e) {
e.printStackTrace();

}
return str;
}

}
public class SHA
{
public static String sha(String str,String type) {
try {
MessageDigest md = MessageDigest.getInstance(type);
md.update(str.getBytes());
byte byteBuffer[] = md.digest();

StringBuffer strHexString = new StringBuffer();
for (int i = 0; i < byteBuffer.length; i++)
{
String hex = Integer.toHexString(0xff & byteBuffer[i]);
if (hex.length() == 1)
{
strHexString.append('0');
}
strHexString.append(hex);
}
// 得到返回結果
str = strHexString.toString();
} catch (Exception e) {
e.printStackTrace();

}
return str;
}
}
六.springmvc.xml
拦截器配置

<!-- 拦截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/"/>
<mvc:mapping path="/v1/**"/>
<bean class="com.demo.interceptors.AuthTokenInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>七.AuthTokenInterceptor.java
 这里只是做了log

public class AuthTokenInterceptor extends HandlerInterceptorAdapter
{
protected Logger logger = LoggerFactory.getLogger(getClass());

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
HttpSession session = request.getSession();

String account = (String) session.getAttribute("currentUser");
if(account==null){
logger.info("{}","请先登录");
return false;
}
Map<String,Object> auth=memberService.authToken(account);
if(auth==null){
logger.info("{}","无此账号");
return false;
}
if(auth.get("auth_token").equals(session.getAttribute("auth_token"))){
long tokenExpire = (System.currentTimeMillis()/1000);
if((int)auth.get("token_expire")
>= tokenExpire){
Map<String,Object> params = new HashMap<>();
params.put("account", account);
params.put("token_expire", tokenExpire);
memberService.updateTokenExpire(params);
logger.info("{}","token正常");
return true;
}else{
logger.info("{}","token过期");
return false;
}

}else{
logger.info("{}","token失效");
return false;
}
//return super.preHandle(request, response, handler);
}

}
八.controller

Controller
@RequestMapping(value="XX")
public class MemberController extends BaseController
{

@RequestMapping(value = "/login", method = {RequestMethod.GET,RequestMethod.POST})
@ResponseBody
public String memberLogin(@RequestParam(value="account",defaultValue="") String account,
@RequestParam(value="password",defaultValue="") String password)
{
UsernamePasswordToken token = new UsernamePasswordToken(account, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (IncorrectCredentialsException ice) {
// 捕获密码错误异常
return "{\"message\":\"password error!\"}";
} catch (UnknownAccountException uae) {
// 捕获未知用户名异常
return "{\"message\":\"account error!\"}";
}
return "{\"message\":\"login\"}";
}
}以上关于memberService的方法调用都与mybatis中的查询一一对应
九.web.xml

网上有很多关于spring springmvc shiro相关的web.xml配置文件信息 再次就不再赘述

注意shiro需要过滤器过滤即可

十.相关jar包下载
http://download.csdn.net/detail/chundonghan/9824537 这里jar包相对较新 可能会有与低版本不兼容的情况
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: