基于权限安全框架Shiro的登录验证功能实现
目前在企业级项目里做权限安全方面喜欢使用Apache开源的Shiro框架或者Spring框架的子框架Spring Security。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。
Shiro框架具有轻便,开源的优点,所以本博客介绍基于Shiro的登录验证实现。
在maven里加入shiro需要的jar
<!--shiro start-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>
<!-- shiro end-->
在web.xml加上Shiro过滤器配置:
<!-- Shiro过滤器配置 start -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Shiro过滤器配置 end -->
编写shiro的ShiroRealm类:
package org.muses.jeeplatform.core.security.shiro;
import javax.annotation.Resource;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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.muses.jeeplatform.model.entity.User;
import org.muses.jeeplatform.service.UserService;
/**
* @description 基于Shiro框架的权限安全认证和授权
* @author Nicky
* @date 2017年3月12日
*/
public class ShiroRealm extends AuthorizingRealm {
/**注解引入业务类**/
@Resource
UserService userService;
/**
* 登录信息和用户验证信息验证(non-Javadoc)
* @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal(); //得到用户名
String password = new String((char[])token.getCredentials()); //得到密码
User user = userService.findByUsername(username);
/**检测是否有此用户 **/
if(user == null){
throw new UnknownAccountException();//没有找到账号异常
}
/**检验账号是否被锁定 **/
if(Boolean.TRUE.equals(user.getLocked())){
throw new LockedAccountException();//抛出账号锁定异常
}
/**AuthenticatingRealm使用CredentialsMatcher进行密码匹配**/
if(null != username && null != password){
return new SimpleAuthenticationInfo(username, password, getName());
}else{
return null;
}
}
/**
* 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
* @see AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
String username = (String)pc.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.getRoles(username));
authorizationInfo.setStringPermissions(userService.getPermissions(username));
System.out.println("Shiro授权");
return authorizationInfo;
}
@Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
@Override
public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
super.clearCachedAuthenticationInfo(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
}
在Spring框架里集成Shiro,加入配置
<!-- Shiro start -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="ShiroRealm" />
</bean>
<!-- 项目自定义的Realm -->
<bean id="ShiroRealm" class="org.muses.jeeplatform.core.security.shiro.ShiroRealm" ></bean>
<!-- Shiro Filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/admin/index" />
<property name="unauthorizedUrl" value="/login" />
<property name="filterChainDefinitions">
<value>
/static/** = anon
/upload/** = anon
/plugins/** = anon
/code = anon
/login = anon
/logincheck = anon
/** = authc
</value>
</property>
</bean>
<!-- Shiro end -->
登录验证控制类实现:
package org.muses.jeeplatform.web.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.muses.jeeplatform.core.Constants;
import org.muses.jeeplatform.model.entity.Menu;
import org.muses.jeeplatform.model.entity.Permission;
import org.muses.jeeplatform.model.entity.Role;
import org.muses.jeeplatform.model.entity.User;
import org.muses.jeeplatform.service.MenuService;
import org.muses.jeeplatform.service.UserService;
import org.muses.jeeplatform.utils.Tools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
/**
* @description 登录操作的控制类,使用Shiro框架,做好了登录的权限安全认证,
* getRemortIP()方法获取用户登录时的ip并保存到数据库
* @author Nicky
* @date 2017年3月15日
*/
@Controller
public class LoginController extends BaseController {
@Autowired
UserService userService;
@Autowired
MenuService menuService;
/**
* 获取登录用户的IP
* @throws Exception
*/
public void getRemortIP(String username) {
HttpServletRequest request = this.getRequest();
Map<String,String> map = new HashMap<String,String>();
String ip = "";
if (request.getHeader("x-forwarded-for") == null) {
ip = request.getRemoteAddr();
}else{
ip = request.getHeader("x-forwarded-for");
}
map.put("username", username);
map.put("loginIp", ip);
userService.saveIP(map);
}
/**
* 访问后台登录页面
* @return
* @throws Exception
*/
@RequestMapping(value="/login",produces="text/html;charset=UTF-8")
public ModelAndView toLogin()throws ClassNotFoundException{
ModelAndView mv = this.getModelAndView();
mv.setViewName("admin/frame/login");
return mv;
}
/**
* 基于Shiro框架的登录验证,页面发送JSON请求数据,
* 服务端进行登录验证之后,返回Json响应数据,"success"表示验证成功
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value="/logincheck", produces="application/json;charset=UTF-8")
@ResponseBody
public String loginCheck(HttpServletRequest request)throws AuthenticationException{
JSONObject obj = new JSONObject();
String errInfo = "";//错误信息
String logindata[] = request.getParameter("LOGINDATA").split(",");
if(logindata != null && logindata.length == 3){
//获取Shiro管理的Session
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
String codeSession = (String)session.getAttribute(Constants.SESSION_SECURITY_CODE);
String code = logindata[2];
/**检测页面验证码是否为空,调用工具类检测**/
if(Tools.isEmpty(code)){
errInfo = "nullcode";
}else{
String username = logindata[0];
String password = logindata[1];
if(Tools.isNotEmpty(codeSession) && codeSession.equalsIgnoreCase(code)){
//Shiro框架SHA加密
String passwordsha = new SimpleHash("SHA-1",username,password).toString();
System.out.println(passwordsha);
//检测用户名和密码是否正确
User user = userService.doLoginCheck(username,passwordsha);
if(user != null){
if(Boolean.TRUE.equals(user.getLocked())){
errInfo = "locked";
}else{
//Shiro添加会话
session.setAttribute("username", username);
session.setAttribute(Constants.SESSION_USER, user);
//删除验证码Session
session.removeAttribute(Constants.SESSION_SECURITY_CODE);
//保存登录IP
getRemortIP(username);
/**Shiro加入身份验证**/
Subject sub = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
sub.login(token);
}
}else{
//账号或者密码错误
errInfo = "uerror";
}
if(Tools.isEmpty(errInfo)){
errInfo = "success";
}
}else{
//缺少参数
errInfo="codeerror";
}
}
}
obj.put("result", errInfo);
return obj.toString();
}
/**
* 后台管理系统主页
* @return
* @throws Exception
*/
@RequestMapping(value="/admin/index")
public ModelAndView toMain() throws AuthenticationException{
ModelAndView mv = this.getModelAndView();
/**获取Shiro管理的Session**/
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
User user = (User)session.getAttribute(Constants.SESSION_USER);
if(user != null){
...//业务实现
}else{
//会话失效,返回登录界面
mv.setViewName("admin/frame/login");
}
mv.setViewName("admin/frame/index");
return mv;
}
/**
* 注销登录
* @return
*/
@RequestMapping(value="/logout")
public ModelAndView logout(){
ModelAndView mv = this.getModelAndView();
/**Shiro管理Session**/
Subject sub = SecurityUtils.getSubject();
Session session = sub.getSession();
session.removeAttribute(Constants.SESSION_USER);
session.removeAttribute(Constants.SESSION_SECURITY_CODE);
/**Shiro销毁登录**/
Subject subject = SecurityUtils.getSubject();
subject.logout();
/**返回后台系统登录界面**/
mv.setViewName("admin/frame/login");
return mv;
}
}
前端Ajax和JQeury校验实现:
/**客户端校验**/
function checkValidity() {
if ($("#username").val() == "") {
$("#username").tips({
side : 2,
msg : '用户名不得为空',
bg : '#AE81FF',
time : 3
});
$("#username").focus();
return false;
}
if ($("#password").val() == "") {
$("#password").tips({
side : 2,
msg : '密码不得为空',
bg : '#AE81FF',
time : 3
});
$("#password").focus();
return false;
}
if ($("#code").val() == "") {
$("#code").tips({
side : 1,
msg : '验证码不得为空',
bg : '#AE81FF',
time : 3
});
$("#code").focus();
return false;
}
return true;
}
/**服务器校验**/
function loginCheck(){
if(checkValidity()){
var username = $("#username").val();
var password = $("#password").val();
var code = username+","+password+","+$("#code").val();
$.ajax({
type: "POST",//请求方式为POST
url: 'logincheck',//检验url
data: {LOGINDATA:code,tm:new Date().getTime()},//请求数据
dataType:'json',//数据类型为JSON类型
cache: false,//关闭缓存
success: function(data){//响应成功
if("success" == data.result){
$("#login").tips({
side : 1,
msg : '正在登录 , 请稍后 ...',
bg : '#68B500',
time : 10
});
window.location.href="admin/index";
}else if("uerror" == data.result){
$("#username").tips({
side : 1,
msg : "用户名或密码有误",
bg : '#FF5080',
time : 15
});
$("#username").focus();
}else if("codeerror" == data.result){
$("#code").tips({
side : 1,
msg : "验证码输入有误",
bg : '#FF5080',
time : 15
});
$("#code").focus();
}else if("locked" == data.result){
alert('您的账号被锁定了,呜呜');
}else{
$("#username").tips({
side : 1,
msg : "缺少参数",
bg : '#FF5080',
time : 15
});
$("#username").focus();
}
}
});
}
}
登录成功,Session会话过期,需要重新登录,保证系统安全性
本博客只提供基于Shiro的登录验证实现,具体代码可以去我的github下载:https://github.com/u014427391/jeeplatform
欢迎star
基于权限安全框架Shiro的登录验证功能实现的更多相关文章
- Java安全(权限)框架 - Shiro 功能讲解 架构分析
Java安全(权限)框架 - Shiro 功能讲解 架构分析 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 简述Shiro Shiro出自公司Apache(阿帕奇),是java的一 ...
- 利用nginx向现有网站添加登录验证功能(不添加修改现有网站代码)
在不改变现有网站代码的前提下加入验证功能: 1.假设现有网站后端nodejs,端口3000,nginx配置如下 server { listen 80; server_name localhost; l ...
- SSM项目使用拦截器实现登录验证功能
SSM项目使用拦截器实现登录验证功能 登录接口实现 public User queryUser(String UserName, String Password,HttpServletRequest ...
- 了解权限控制框架shiro 之实际应用.
Apache Shiro 1.权限控制分为 a.粗粒度 URL 级别权限控制 b.细粒度方法级别权限控制 2.使用shiro进行权限控制主要有四种主要方式 : a. 在程序中 通过 Subje ...
- 如何使用新浪微博账户进行应用登录验证(基于Windows Azure Mobile Service 集成登录验证)
使用三方账号登录应用应该对大家来说已经不是什么新鲜事儿了,但是今天为什么还要在这里跟大家聊这个话题呢,原因很简单 Windows Azure Mobiles Service Authenticatio ...
- 权限控制框架Shiro简单介绍及配置实例
Shiro是什么 http://shiro.apache.org/ Apache Shiro是一个非常易用的Java安全框架,它能提供验证.授权.加密和Session控制.Shiro非常轻量级,而且A ...
- 权限控制框架---shiro入门
1.shiro控制权限的两种思想:粗粒度url级别,细粒度方法级别 2.shiro执行流程简介 3.案例 3.1shiro控制用户登录实现,登录其实就是shiro中的认证. (1)配置web.xml( ...
- django自带的登录验证功能
django自带的验证机制 from django.shortcuts import render, redirect from django.contrib.auth import authenti ...
- ASP.NET Core 一步步搭建个人网站(4)_主页和登录验证
上章节我们已经定制好动态配置的菜单,用户登录网站的第一步就是进入首页内容,那我们先搭建一下我们的首页内容.想着自己的网站内容主要是个人博客类型,所以,首页就展示博主本人的一些基本信息吧,哈哈.当然,做 ...
随机推荐
- C#PreviewKeyDown 与KeyDown 区别
PreviewKeyDown:在焦点位于此控件上的情况下,当有按键动作时发生(在 KeyDown 事件之前发生). 小注: 某些按键,比如 Tab.Return.Esc 和箭头键,通常会被某些控件忽略 ...
- [转载] Java并发编程:Lock
转载自http://www.cnblogs.com/dolphin0520/p/3923167.html 以下是本文目录大纲: 一.synchronized的缺陷 二.java.util.concur ...
- [转载] Hibernate与 MyBatis的比较
转载自http://blog.csdn.net/firejuly/article/details/8190229 最近做了一个Hibernate与MyBatis的对比总结,希望大家指出不对之处. 第一 ...
- Java build path && Deployment assembly && 编译路径 && 发布路径
java build path java源文件,编译后,输出的路径,默认值为: *此时的源码文件夹在 /src deployment assembly 系统发布路径设置,将完成(或未完成)的项目对应的 ...
- Entity Framework——常见报错总结
1 实体属性配置为IsRequired()对更新的影响 抛出异常类型DbEntityValidationException 表结构: 实体: public class User { public in ...
- SQL SERVER 2012 SEQUENCE
一.Sequence简介 Sequence对象对于Oracle用户来说是最熟悉不过的数据库对象了, 在SQL SERVER2012终于也可以看到这个对象了.Sequence是SQL Server201 ...
- ubuntu下mysql提示Changed limits: max_open_files:1024解决办法
在配置我的md5解密网站cmd5.la的时候,mysql5.7出现了max_open_files: 1024, max_connections: 214,warning: Changed limits ...
- C# winform 程序开发知识点总结(干货)
1.数据库连接及操作 在说数据库操作之前,先说一下数据库连接操作字符串的获取 首先,点击服务器资源管理器,接下来选中数据连接右键点击添加连接,填入你要连接的服务器名称,点击单选框使用SQL Serve ...
- 在JQuery中如何获取当前时间?
////发表时间(now) function p(s) { return s < 10 ? '0' + s : s; } var myDate = new Date(); //获取当前年 var ...
- IPhoneX网页布局
IPhoneX全面屏是十分科技化的,但是由于其圆角和摄像头刘海位置以及操控黑条的存在使得我们需要去对其样式做一些适配,没有X的同学可以开启 Xcode 9 的iPhone X 模拟器作为学习和调试. ...