首先是配置:
pom.xml:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.1</version>
</dependency>
web.xml:
<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.xml:
<!--Shiro的Web业务应用程序的主要业务层对象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myShiroRealm" />
<property name="cacheManager" ref="cacheManager" />
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
<!--记住我管理 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="remeberMeCookie"/><!--注入cookie类-->
<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/><!--加密的cookie值-->
</bean>
<!-- 項目自定义的Realm -->
<bean id="myShiroRealm" class="com.gao.test.shiro.MyRealm">
<property name="cacheManager" ref="cacheManager" />
</bean>
<!-- Shiro 的过滤层-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.jsp" /> <!--登陆页面-->
<property name="successUrl" value="/index.jsp"/> <!--登陆成功跳转,或者成功跳转到指定-->
<property name="unauthorizedUrl" value="/login.jsp" /><!--授权失败跳转到登陆-->
<property name="filters">
<map>
<entry key="rememberMe" value-ref="rememberFilter"></entry><!--这里加了一个自己写的表单过滤验证-->
</map>
</property>
<property name="filterChainDefinitions">
<value>
/success = authc <!--authc是需要账号密码正确后能访问-->
/succ = user,rememberMe <!--user是可以用记住我访问-->
/login = anon <!--这里不需要授权就能访问-->
/login.jsp = anon
/logout.jsp = anon
/index = anon
/logout = anon
/** = user
</value>
</property>
</bean>
<!-- 用户授权信息Cache -->
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- AOP式方法级权限检查,注解方式 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!--防止记住我登陆后没有session,自己写的表单过滤器-->
<bean id="rememberFilter" class="com.gao.test.shiro.RememberRealm"/>
<!--记住我cookie的设置-->
<bean id="remeberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/> <!--cookie名字-->
<property name="path" value="/"/> <!--coolie存储在浏览器位置-->
<property name="httpOnly" value="true"/> <!--js脚本无法读取cookie,防止XSS攻击-->
<property name="maxAge" value="604800"/> <!--存储时间-->
</bean>
代码:
realm授权规则:
public class MyRealm extends AuthorizingRealm {
private static final String USER_NAME = "gao"; //测试用的账号密码
private static final String PASSWORD = "123";
//登陆后添加角色和权限用的
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Set<String> roleNames = new HashSet<String>();
Set<String> permissions = new HashSet<String>();
roleNames.add("admin");//添加角色
permissions.add("success.jsp"); //添加权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
info.setStringPermissions(permissions);
return info;
}
//登陆验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//拿到账号密码令牌
UsernamePasswordToken ut = (UsernamePasswordToken) token;
String username = ut.getUsername();
//这里只需要验证一下账号是否存在即可,然后通过数据库拿到密码(或者涉及到盐的密码)
if(username.equals(USER_NAME)){
//这里就直接把账号,查到的密码,还有类名交给shiro,他会自己去验证是否正确
return new SimpleAuthenticationInfo(username,PASSWORD,getName());
}else{
//这里抛出没有用户的错误
throw new AuthenticationException();
}
}
}
表单过滤:
public class RememberRealm extends FormAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
//这里的目的是通过记住我登陆后,从shiro中拿到账号id或者用户名,然后查询到用户信息,放到session
Subject subject = getSubject(request, response);
Session session = subject.getSession();
Object principal = subject.getPrincipal();
session.setAttribute("user",principal.toString());
return subject.isRemembered()||subject.isAuthenticated();
}
}
web处理层:
@Controller
public class IndexController {
//这里如果强制进入登录页,则退出从新登录
@RequestMapping("/index")
public String index() {
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
subject.logout();
}
return "login";
}
//测试用
@RequestMapping("/succ")
public String success() {
return "succ";
}
//登录页面
@RequestMapping("/login")
public String login(String user, String pass, String remember,HttpServletRequest req) {
try {
UsernamePasswordToken token = new UsernamePasswordToken(user, pass, (remember == null || remember == "") ? false : true);
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
subject.login(token);
}
HttpSession session = req.getSession();
session.setAttribute("user",user);
} catch (Exception e) {
System.out.println("登录失败");
return "error";
}
return "success";
}
//这里退出功能,并且能够清楚浏览器cookie
@RequestMapping("/logout")
public String logout(HttpServletRequest re, HttpServletResponse res) {
Cookie[] cookies = re.getCookies();
if (cookies != null) {
Arrays.stream(cookies).forEach(
cookie -> {
if (cookie.getName().equals("rememberMe")) {
Cookie rem = new Cookie("rememberMe", null);
rem.setPath("/");
rem.setMaxAge(0);
res.addCookie(rem);
}
}
);
}
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated()){
subject.logout();
}
return "logout";
}
}
下面是使用ajax方式登陆:
shiro会走自己的验证,不需要你在controller写登陆接口了.下面是设置规则和接收参数
shiro.xml:
<bean id="ajax" class="com.gao.test.shiro.AjaxRealm">
<property name="usernameParam" value="user"/> <!--这里的value是你表单提交的字段-->
<property name="passwordParam" value="pass"/>
<property name="rememberMeParam" value="rememberMe"/> <!--这里接收的值是一个boolean类型-->
<property name="loginUrl" value="/login"/> <!--登陆地址,通过这个地址判断是不是进行登陆操作-->
</bean>
<property name="filters">
<map>
<entry key="ajaxRealm" value-ref="ajax"></entry> <!--加入到过滤链-->
</map>
</property>
<property name="filterChainDefinitions">
<value>
/login = ajaxRealm <!--这里的要与自定义过滤器名称相同,才会走自定义的过滤器-->
/success = authc
/succ = authc,rememberMe
/login.jsp = anon
/logout.jsp = anon
/index = anon
/logout = anon
/** = authc
</value>
</property>
下面是代码:
//继承表单过滤器
public class AjaxRealm extends FormAuthenticationFilter {
//这里是没有登陆时首先运行这里(这里可以依据需求自己更改)
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//返回没有登陆信息
httpServletResponse.setContentType("application/json; charset=utf-8");
//判断请求地址是否为/login
if(this.isLoginRequest(request, response)) {
//这里判断是否为post方式
if(this.isLoginSubmission(request, response)) {
//是post方式,则进行登陆
return this.executeLogin(request, response);
} else {
/* PrintWriter writer = httpServletResponse.getWriter();
PrintWriter out = httpServletResponse.getWriter();
out.println("{\"success\":fail,\"message\":\"不能用get请求\"}");
out.flush();
out.close();
return false;*/
//或者用通过get登陆也验证一下代码
return this.executeLogin(request, response);
}
} else {
//如果请求地址不是登陆,则响应信息
PrintWriter writer = httpServletResponse.getWriter();
PrintWriter out = httpServletResponse.getWriter();
out.println("{\"success\":fail,\"message\":\"没有登陆\"}");
out.flush();
out.close();
return false;
}
}
/**
* 当登录成功
* @param token
* @param subject
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if (!"XMLHttpRequest".equalsIgnoreCase(httpServletRequest
.getHeader("X-Requested-With"))) {// 不是ajax请求
issueSuccessRedirect(request, response);
} else {
httpServletResponse.setCharacterEncoding("UTF-8");
PrintWriter out = httpServletResponse.getWriter();
out.println("{\"success\":true,\"message\":\"登入成功\"}");
out.flush();
out.close();
}
return false;
}
/**
* 当登录失败
* @param token
* @param e
* @param request
* @param response
* @return
*/
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if (!"XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request)
.getHeader("X-Requested-With"))) {// 不是ajax请求
//setFailureAttribute(request, e);
try {
httpServletResponse.sendRedirect("/login.jsp");
} catch (IOException e1) {
e1.printStackTrace();
}
return false;
}
try {
httpServletResponse.setContentType("text/html; charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
String message = e.getClass().getSimpleName();
if ("IncorrectCredentialsException".equals(message)) {
out.println("{\"success\":false,\"message\":\"密码错误\"}");
} else if ("UnknownAccountException".equals(message)) {
out.println("{\"success\":false,\"message\":\"账号不存在\"}");
} else if ("LockedAccountException".equals(message)) {
out.println("{\"success\":false,\"message\":\"账号被锁定\"}");
} else if("AuthenticationException".equals(message)){
out.println("aa");
} else {
out.println("{\"success\":false,\"message\":\"未知错误\"}");
}
out.flush();
out.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return false;
}
}