本文建立在 SSH与Spring Security整合 一文的基础上,从这篇文章的example上做修改,或者从 配置了AOP 的example上做修改皆可。这里主要补充我在实际使用Spring Security中常用的一些前文最基本example中没能提供的功能,主要包括自定义403错误页面自定义认证管理器的内容提供者自定义登录成功的回调接口自定义json访问时未登录和403错误的返回内容用代码模拟Spring Security的验证

1. 自定义权限不够时访问的界面

在搭建Spring Security的时候,http标签内配置了这样的子标签:

<form-login
login-page="/"
default-target-url="/"
authentication-failure-url="/?login=error" />

这个属性是说,如果待访问的资源需要一定的权限,但是当前用户没有登录,那么应该跳转到login-page上去登录,如果登录成功了,就跳转到default-target-url上去,如果登录失败了,就跳转到anthentication-failure-url上去,但是缺一个配置,那就是如果我登录了,并且是USER权限,现在访问了一个需要ADMIN权限的资源,那么怎么办?实际中会返回一个默认的界面:

那么这个界面太丑了,怎么自定义,这个非常简单,只需要在http标签中加入下面的一个:

<access-denied-handler error-page="/denied"/>

也就是说,如果访问权限不够,就会访问/denied这个资源,因为Springmvc会拦截所有的请求,这个也不例外,在HomeController中加入:

@RequestMapping("/denied")
public String denied(){
return "denied";
}

在webapps/pages目录下创建denied.jsp:

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <c:set var="base" value="${pageContext.request.contextPath }/" scope="session"/> <html>
<body>
<h2>您的访问权限不够!!</h2>
<h3>3秒钟之后跳转到首页。。。或点击<a href="${base }">首页</a></h3>
</body>
<script type="text/javascript">
setTimeout(function(){
location.href = "${base }";
}, 3000);
</script>
</html>

再次访问受限的资源就会跳转到这个界面上。

2. 自己定义认证管理器的内容提供者

先回顾一下前文中怎么做用户名密码验证的:

<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username, password, 1 from user where username = ?"
authorities-by-username-query="select u.username, r.role from user u left join role r on u.role_id=r.id where username = ?"
/>
</authentication-provider>
</authentication-manager>

指定数据源,根据用户提交上来的用户名发两条sql语句,获取到password和role,然后拿password和用户提交的密码(根据配置可能会做加盐的处理)匹配,如果登录成功,该用户的信息就以role所代表的权限保存了起来,但是有时候,对用户名密码的获取,不能够通过简单的两条sql语句来获取,那又应该怎么办呢?这就需要我们来自定义了,基本思路是我们写一个bean,Spring把用户名给这个bean,这个bean自己去找密码权限应该是什么,最后封装成一个User对象返回给Spring,也就是说,我们需要写自己的jdbc-user-service。下面就来实现它,创建一个package叫做security,再写一个类EssentialUser,并实现Spring的UserDetails接口,这个类就是Spring最终需要的User对象:

package org.zhangfc.demo4ssh.security;

import ......;

public class EssentialUser implements UserDetails {

    private static final long serialVersionUID = -3369448632273314162L;
private int id;
private String role;
private String username;
private String password; public EssentialUser(User user) {
this.id = user.getId();
this.role = user.getRole().getRole();
this.username = user.getUsername();
this.password = user.getPassword();
} // setter and getter of id, role
// setter of username, password @Override
public String getUsername() {
return username;
} @Override
public String getPassword() {
return password;
} @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auths = new ArrayList<>();
auths.add(new SimpleGrantedAuthority(this.role));
return auths;
} @Override
public boolean isAccountNonExpired() {
return true;
} @Override
public boolean isAccountNonLocked() {
return true;
} @Override
public boolean isCredentialsNonExpired() {
return true;
} @Override
public boolean isEnabled() {
return true;
}
}

下面需要写一个Service来把这个对象给Spring,还是在security包下面创建MyUserDetailsService,实现Spring的UserDetailsService接口,我这儿简单起见了,我就只new了一个User对象,实际上应该是查询好了必要信息的对象:

public class MyUserDetailsService implements UserDetailsService {

    @Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User u = new User(); // 根据username来得到User对象
EssentialUser eu = new EssentialUser(u);
return eu;
} }

剩下的就很简单了,注册一下这个bean,并把它作为认证信息的提供者:

<beans:bean id="userDetails" class="cn.edu.tju.ina.estuary.security.MyUserDetailsService" />

<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetails" />
</authentication-manager>

3. 自定义登录成功之后的回调

有时候,Spring存的那个UserDetails用户信息不全,而且因为是Spring的接口,有时候用起来也不方便,我们希望在登录成功之后再在session中存一份当前用户对象,登录成功之后Spring会跳转到配置的URL上,但是很多时候,登录成功就是跳回首页,访问首页没必要再分是不是刚登录,所以要是Spring Security有登录之后的回调接口,存session的工作就可以在那里做了,这个想法当然是可行的。在security这个package下创建类AfterAuthSuccess,继承SimpleUrlAuthenticationSuccessHandler:

public class AfterAuthSuccess extends SimpleUrlAuthenticationSuccessHandler {

    @Autowired
private UserService userService; @Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
RequestCache requestCache = new HttpSessionRequestCache();
SavedRequest savedRequest = requestCache.getRequest(request, response); HttpSession session = request.getSession();
SecurityContext sc = SecurityContextHolder.getContext();
String userName = sc.getAuthentication().getName();
User u = userService.findByUsername(userName);
session.setAttribute("currentUser", u); if (savedRequest == null) {
// if click login to open login page, savedRequest will be null.
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
String targetUrl = savedRequest.getRedirectUrl();
if(targetUrl != null && "".equals(targetUrl)){
super.onAuthenticationSuccess(request, response, authentication);
return;
}
getRedirectStrategy().sendRedirect(request, response, targetUrl);
} }

这段代码可以直接用,看上去很复杂,是因为考虑了一些情况,比如访问A页面发现没有登录,这时候会跳转到登录页面去登录,登录成功之后会直接跳到A上去。

然后在xml文件中配置一下这个bean:

<http auto-config="true">
<intercept-url pattern="/admin**" access="ROLE_ADMIN" />
<form-login
login-page="/"
authentication-success-handler-ref="authSuccess"
default-target-url="/"
authentication-failure-url="/?login=error" />
<access-denied-handler error-page="/denied"/>
<logout logout-success-url="/" />
</http> <beans:bean id="authSuccess" class="org.zhangfc.demo4ssh.security.AfterAuthSuccess" />

4. 自定义json访问时未登录和403错误的返回内容

web应用中有很多接口可能是为移动端设计的,移动端有自己的权限控制方案,或者web也可能频繁请求json资源,那么对这些接口,未登录的时候就不能再跳转到login-page,权限不够的时候也不能再返回个403页面,这就需要自己来配置,原来有一个http标签,用来处理所有的请求,现在在它前面加一个,只处理/json开头的地址:

<http pattern="/json**" entry-point-ref="jsonEntryPoint">
<intercept-url pattern="/json**" access="ROLE_USER" />
<access-denied-handler error-page="/900" />
</http>

只有ROLE_USER权限是可以访问这些资源的(ROLE_ADMIN也不行),如果是权限不够呢,跳转到/900,如果是未登录,也就是Spring Security没有存这个票据,那么Spring会扔出一个异常,扔到ExceptionTranslationFilter链里去,EntryPoint就是来处理这个问题的,来看这个引用的bean:

<beans:bean id="jsonEntryPoint" class="org.zhangfc.demo4ssh.security.JsonEntryPoint">
<beans:property name="url" value="/901"></beans:property>
</beans:bean>

这儿指定当未登录的时候请求/901。看看这个bean怎么来实现:

public class JsonEntryPoint implements AuthenticationEntryPoint {

    private String url = "/";

    @Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
request.getRequestDispatcher(url).include(request, response);
} public void setUrl(String url) {
this.url = url;
} }

非常简单,这个AuthenticationException还可以拿来做一些更细致的判断,不过我没有去做太多尝试。

最后只要在/900和/901的Controller里面返回对应的json串就可以了。

5. 代码模拟登录

前面的介绍都是Spring自己去校验用户名密码之后就登录了,有时候我们需要模拟Spring登录,比如注册之后直接变成登录状态,当然也可以用代码发一个登录请求,不过有些麻烦,不如直接用代码来登录,其实也很简单:

@Autowired
@Qualifier("authenticationManager")
protected AuthenticationManager authenticationManager; private void setAuthInSpringSecuity(String username, String password,
HttpServletRequest request) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
username, password);
try {
token.setDetails(new WebAuthenticationDetails(request));
Authentication authenticatedUser = authenticationManager
.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(
authenticatedUser);
request.getSession()
.setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
} catch (AuthenticationException e) {
System.out.println("Authentication failed: " + e.getMessage());
}
}

这儿就只把代码贴在这里了,没有什么需要解释的,Spring验证登录成功之后会把当前用户对象放到session里,最后几行做的就是这个事情。

【JavaEE】SSH+Spring Security自定义Security的部分处理策略的更多相关文章

  1. 【JavaEE】SSH+Spring Security基础上配置AOP+log4j

    Spring Oauth2大多数情况下还是用不到的,主要使用的还是Spring+SpringMVC+Hibernate,有时候加上SpringSecurity,因此,本文及以后的文章的example中 ...

  2. Spring Security 自定义登录页面

    SpringMVC + Spring Security,自定义登录页面登录验证 学习参考:http://www.mkyong.com/spring-security/spring-security-f ...

  3. Spring Security 自定义登录认证(二)

    一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Se ...

  4. (二)spring Security 自定义登录页面与校验用户

    文章目录 配置 security 配置下 MVC 自定义登录页面 自定义一个登陆成功欢迎页面 效果图 小结: 使用 Spring Boot 的快速创建项目功能,勾选上本篇博客需要的功能:web,sec ...

  5. spring security自定义指南

    序 本文主要研究一下几种自定义spring security的方式 主要方式 自定义UserDetailsService 自定义passwordEncoder 自定义filter 自定义Authent ...

  6. 解决Spring Security自定义filter重复执行问题

    今天做项目的时候,发现每次拦截器日志都会打两遍,很纳闷,怀疑是Filter被执行了两遍.结果debug之后发现还真是!记录一下这个神奇的BUG! 问题描述 项目中使用的是Spring-security ...

  7. spring boot + thymeleaf +security自定义规则 的简单使用

    1.前言 以前开发一直使用 springMVC模式开发 ,前端页面常使用 JSP  ,现在html5淘汰了 ,要么使用html ,要么使用vue , 现在使用spring boot ,有必要总结一下 ...

  8. spring boot之security

    上一节的时候,我们打开了springboot的端点,有一些数据是非常敏感的,比如/shutdown. 这一节,我们要给一些敏感信息加上权限控制. spring boot本身的security模块就很好 ...

  9. 初识Spring security-添加security

    请先查看 初识Spring security-无Security的SpringMVC 在pom.xml文件中添加包 <!-- Spring Security --> <depende ...

随机推荐

  1. 实现让Lync client也能够"潜水"隐身聊天

    看到MSN或QQ,都支持隐身聊天. Lync Server  2013也是支持的.   1.Server端:Lync 2013 Server 缺省策略是没有设置显示脱机功能.(设置前截图)   2.直 ...

  2. 二十七(序幕)、【开源】EFW框架破茧成蝶

    回<[开源]EFW框架系列文章索引>        EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://p ...

  3. IOS8Preview-xCode_6

    IOS8Preview-xCode_6 what's new What's new in xCode 6 Xcode 6 introduces a radically new way to desig ...

  4. react-native SyntaxError xxxxx/xx.js:Unexpected token (23:24)

    在运行react-native项目时提示 SyntaxError xxxxx/xx.js:Unexpected token (23:24) 我这边的问题原因:jsx语法错误,解决办法就是认真排查代码然 ...

  5. ASP.NET MVC 4 Web编程

    http://spu.jd.com/11309606.html 第1章 入门第2章 控制器第3章 视图第4章 模型第5章 表单和HTML辅助方法第6章 数据注解和验证第7章 成员资格.授权和安全性第8 ...

  6. HIVE: UDF应用实例

    数据文件内容 TEST DATA HERE Good to Go 我们准备写一个函数,把所有字符变为小写. 1.开发UDF package MyTestPackage; import org.apac ...

  7. MyBatis知多少(14)分散的数据库系统

    任何一个重要的数据库无疑都会拥有不止一个依赖者.即使该数据库只是简单地被两个Web 应用程序所共享,也有许多事情需要考虑.假设有一个名为网上购物车的Web应用程序,它使用了一个包含类别代码的数据库.就 ...

  8. 【转】github上值得关注的前端项目

    综合/资源 frontend-dev-bookmarks 一个巨大的前端开发资源清单.star:15000 front-end-collect 分享自己长期关注的前端开发相关的优秀网站.博客.以及活跃 ...

  9. 两分钟了解REACTIVEX

    可能在之前,你就已经看过这篇响应式编程的入门.什么?太长?好吧,这都没关系,Rx并不难,你甚至可以自己实现一个这样的框架. 知道数组吧?你当然知道,这就是: [ 14, 9, 5, 2, 10, 13 ...

  10. 3、使用Oracle Logminer同步Demo

    使用Oracle Logminer同步Demo 1 Demo介绍 1.1 Demo设想 前面介绍了Oracle LogMiner配置使用以及使用LogMiner进行解析日志文件性能,在这篇文章中将利用 ...