`

我的SpringSecurity实践

阅读更多
我的SpringSecurity实践

(一) 数据库与实体类设计(mysql)

-- 权限
DROP TABLE IF EXISTS `me`.`tbl_permission` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_permission` (
  `_id` INT NOT NULL AUTO_INCREMENT ,
  `_name` VARCHAR(45) NOT NULL ,
  `_desc` VARCHAR(255) NULL ,
  PRIMARY KEY (`_id`) ,
  UNIQUE INDEX `_name_UNIQUE` (`_name` ASC) )
ENGINE = InnoDB;

-- 角色
DROP TABLE IF EXISTS `me`.`tbl_role` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_role` (
  `_id` INT NOT NULL AUTO_INCREMENT ,
  `_name` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`_id`) ,
  UNIQUE INDEX `_name_UNIQUE` (`_name` ASC) )
ENGINE = InnoDB;

-- 角色权限关联表
DROP TABLE IF EXISTS `me`.`tbl_role_permission` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_role_permission` (
  `_id_permission` INT NOT NULL ,
  `_id_role` INT NOT NULL ,
  PRIMARY KEY (`_id_permission`, `_id_role`) ,
  INDEX `fk_permission_QENOQPN` (`_id_permission` ASC) ,
  INDEX `fk_role_PQEMAQWENGHJ` (`_id_role` ASC) ,
  CONSTRAINT `fk_permission_QENOQPN`
    FOREIGN KEY (`_id_permission` )
    REFERENCES `me`.`tbl_permission` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_role_PQEMAQWENGHJ`
    FOREIGN KEY (`_id_role` )
    REFERENCES `me`.`tbl_role` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- 用户表
DROP TABLE IF EXISTS `me`.`tbl_user` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_user` (
  `_id` INT NOT NULL AUTO_INCREMENT ,
  `_username` VARCHAR(45) NOT NULL ,
  `_password` CHAR(32) NULL ,
  `_disabled` CHAR(1) NULL ,
  PRIMARY KEY (`_id`) ,
  UNIQUE INDEX `_username_UNIQUE` (`_username` ASC) ,
  UNIQUE INDEX `_disabled_UNIQUE` (`_disabled` ASC) )
ENGINE = InnoDB;

-- 用户Email表 跟SpringSecurity没有关系
DROP TABLE IF EXISTS `me`.`tbl_email` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_email` (
  `_user_id` INT NOT NULL ,
  `_email` VARCHAR(70) NOT NULL ,
  `_order` INT NOT NULL ,
  PRIMARY KEY (`_user_id`, `_email`) ,
  UNIQUE INDEX `_email_UNIQUE` (`_email` ASC) ,
  INDEX `fk_user_id_QWZLAKUIG` (`_user_id` ASC) ,
  CONSTRAINT `fk_user_id_QWZLAKUIG`
    FOREIGN KEY (`_user_id` )
    REFERENCES `me`.`tbl_user` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- 用户角色关联表
DROP TABLE IF EXISTS `me`.`tbl_user_role` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_user_role` (
  `_user_id` INT NOT NULL ,
  `_role_id` INT NOT NULL ,
  PRIMARY KEY (`_user_id`, `_role_id`) ,
  INDEX `fk_user_id_POIUYHJNB` (`_user_id` ASC) ,
  INDEX `fk_role_id_MNHTRFVD` (`_role_id` ASC) ,
  CONSTRAINT `fk_user_id_POIUYHJNB`
    FOREIGN KEY (`_user_id` )
    REFERENCES `me`.`tbl_user` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_role_id_MNHTRFVD`
    FOREIGN KEY (`_role_id` )
    REFERENCES `me`.`tbl_role` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

显然这种设计方式实际上比较常见的,用户与角色是多对多关系;角色与权限也是多对多关系。

下面是实体类(为了节约篇幅getter和setter以及ORM相关的元注释略去)
import java.io.Serializable;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;

/** 权限类实现 GrantedAuthority接口 */
public class Permission implements Serializable, GrantedAuthority {
	private Integer id;
	private String name;
	private String description;
	private Set<Role> roles;

	public String getAuthority() {
		return getName();
	}
}

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;

/** 角色类 */
public class Role implements Serializable {
	private Integer id;
	private String name;
	private Set<Permission> permissions = new HashSet<Permission>();
	private Set<User> users = new HashSet<User>();
	
	// 为了简便起见 ROLE 和 Permission都视为一种权限
	public GrantedAuthority generateGrantedAuthority() {
		return new GrantedAuthority() {
			public String getAuthority() {
				return getName();
			}
		};
	}
}

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/** 用户类实现UserDetails接口 */
public class User implements Serializable, UserDetails {
	private Integer id;
	private String username;
	private String password;
	private List<String> emails = new ArrayList<String>();	// 和SpringSecurity没有关系的业务字段
	private String disabled;
	private Set<Role> roles = new HashSet<Role>();

	public Collection<? extends GrantedAuthority> getAuthorities() {
		List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
		
		for (Role role : roles) {
			list.add(role.generateGrantedAuthority());
			for (Permission permission : role.getPermissions()) {
				list.add(permission);
			}
		}
		// 排序其实没有必要
		// Collections.sort(list, GrantedAuthorityComparators.REVERSE);
		return list;
	}

	public String getPassword() {
		return password;
	}

	public String getUsername() {
		return username;
	}

	public boolean isAccountNonExpired() {
		return true;
	}

	public boolean isAccountNonLocked() {
		return true;
	}

	public boolean isCredentialsNonExpired() {
		return true;
	}

	public boolean isEnabled() {
		return disabled.equalsIgnoreCase("F");
	}
}

class GrantedAuthorityComparators implements Comparator<GrantedAuthority> {
	
	public static final Comparator<GrantedAuthority> DEFAULT = new GrantedAuthorityComparators();
	
	public static final Comparator<GrantedAuthority> REVERSE = new Comparator<GrantedAuthority>() {
		public int compare(GrantedAuthority o1, GrantedAuthority o2) {
			return - DEFAULT.compare(o1, o2);
		}
	};

	private GrantedAuthorityComparators() { super(); }

	public int compare(GrantedAuthority g1, GrantedAuthority g2) {
		return g1.getAuthority().compareTo(g2.getAuthority());
	}
}


(二) 编写重要的UserDetailsService实现类
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.yingzhuo.me.domain.User;

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public interface UserDao extends UserDetailsService {
	public User findUserByUsernameAndPassword(String username, String password);
}

import org.hibernate.Query;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;

@Repository("userDao")
public class UserDaoImpl extends BaseDao implements UserDao {

	public User findUserByUsernameAndPassword(String username, String password) {
		final String hql = "from User as u left join fetch u.roles where u.username = :username and u.password = :password";

		Query query = getSession().createQuery(hql)
				.setParameter("username", username)
				.setParameter("password", password);
		return (User) query.uniqueResult();
	}

	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		final String hql = "from User as u left join fetch u.roles where u.username = :username";

		Query query = getSession().createQuery(hql).setParameter("username", username);
		User user = (User) query.uniqueResult();

		if (user == null) {
			throw new UsernameNotFoundException("Username '" + username + "' not found");
		}

		// 取消延迟加载
		for (Role role : user.getRoles()) {
			for (Permission per : role.getPermissions()) {
				per.getName();
			}
		}
		return user;
	}
}

虽然SpringSecurity框架提供的接口很多,真正要亲自实现的不多三个而已
  • org.springframework.security.core.GrantedAuthority
  • org.springframework.security.core.userdetails.UserDetails
  • org.springframework.security.core.userdetails.UserDetailsService


(三) 编写Spring Security 配置文件
尽管SpringSecurity提供了<http>元素来简化配置,简化过后有相当多的细节被隐藏起来了。
有时候想改变一下框架的默认行为十分不便。我还是用传统的Bean方式。
这样虽然麻烦,但是掌握之后,一旦看哪个bean不顺眼就可以方便的取而代之。强大灵活!

3.0 xml的schema, 因为是用bean的方式配置嘛,默认命名空间当然是用beans方便。
<?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:p="http://www.springframework.org/schema/p"
	xmlns:c="http://www.springframework.org/schema/c"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:security="http://www.springframework.org/schema/security"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
</beans>


3.1 web项目部署描述
<!-- Spring Security 核心拦截器组 -->
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Web Application Context -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		classpath:spring-bean.xml
		classpath:spring-orm.xml
		classpath:spring-security.xml
	</param-value>
</context-param>

<!-- 支持SpringSecurity Session并发控制 -->
<listener>
	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

提醒一下,如果SpringSecurity和Struts2共同使用的话 org.springframework.web.filter.DelegatingFilterProxy 一定要配置在
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter前面。要不然你的SpringSecurity根本不会起任何作用的。
当然如果使用的是 SpringMVC框架的话那根本无所谓配置顺序,因为SpringMVC的核心转发器是一个Servlet的实现。


3.2 配置SpringSecurity过滤器组 (我一般就用这九个) 顺序很重要
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
	<security:filter-chain-map request-matcher="ant" >
	<security:filter-chain pattern="/**" filters="
	   channelProcessingFilter,
           concurrencyFilter,
           securityContextPersistenceFilter,
           logoutFilter,
           usernamePasswordProcessingFilter,
           rememberMeProcessingFilter,
           anonymousProcessingFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
	</security:filter-chain-map>
</bean>


3.3 channelProcessingFilter: 常用来将某些HTTP协议的URL重定向到HTTPS协议
<bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
	<property name="channelDecisionManager" ref="channelDecisionManager" />
	<property name="securityMetadataSource">
		<security:filter-security-metadata-source request-matcher="ant">
			<!--
			<security:intercept-url pattern="/just/test" access="REQUIRES_SECURE_CHANNEL" />
			-->
			<security:intercept-url pattern="/**" access="ANY_CHANNEL" />
		</security:filter-security-metadata-source>
	</property>
</bean>

<bean id="channelDecisionManager"
	class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
	<property name="channelProcessors">
		<list>
			<ref local="secureChannelProcessor" />
			<ref local="insecureChannelProcessor" />
		</list>
	</property>
</bean>

<bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor" />
<bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor" />


3.4 concurrencyFilter:HttpSession并发过滤器
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
	<property name="sessionRegistry" ref="sessionRegistry" />
	<property name="expiredUrl" value="/common/session-expired" />	<!-- 配置Session过期后重定向的地址 -->
</bean>

<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
	<constructor-arg index="0" ref="sessionRegistry" />
	<property name="maximumSessions" value="1" />
</bean>


3.5 securityContextPersistenceFilter:获取或存储一个SecurityContext
<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter" />


3.6 logoutFilter:监控一个实现退出功能的URL
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
	<constructor-arg index="0" value="/common/login" />	<!-- 退出后重定向 -->
	<constructor-arg index="1">
		<array>
			<ref local="logoutHandler" />
			<ref local="rememberMeServices" />
		</array>
	</constructor-arg>
	<property name="filterProcessesUrl" value="/common/logout"/> <!-- 监控的URL -->
</bean>

<!-- 这个Bean注入到logoutFilter中去,它实际负责最后的扫尾工作,如把HttpSession实例删除 -->
<bean id="logoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
	<property name="invalidateHttpSession" value="true" />
</bean>

这个bean没有没有默认构造方法。

3.7 usernamePasswordProcessingFilter:处理用户登录请求
<bean id="usernamePasswordProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
	<property name="filterProcessesUrl" value="/common/login-process"/>
	<property name="usernameParameter" value="username"/>
	<property name="passwordParameter" value="password"/>
	<property name="authenticationManager" ref="customAuthenticationManager"/>
	<property name="rememberMeServices" ref="rememberMeServices"/>
	<property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
	<property name="sessionAuthenticationStrategy" ref="sas" />
</bean>

<!--
	这个Bean注入到usernamePasswordProcessingFilter中去,他决定用户名和密码验证失败之后的动作
	注意: 应设置行为为转发方式,否则保存在HttpServletRequest实例中的错误信息会因为重定向而丢失。
 -->
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
	<property name="defaultFailureUrl" value="/common/login"/>
	<property name="useForward" value="true" />
</bean>


3.8 rememberMeProcessingFilter: 实现"记住我"功能
<bean id="rememberMeProcessingFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
	<property name="rememberMeServices" ref="rememberMeServices"/>
	<property name="authenticationManager" ref="customAuthenticationManager" />
</bean>

<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
	<property name="key" value="#{securityKeys['remember-me']}" />	<!-- KEY 用于加密 两个一定要相同 -->
	<property name="parameter" value="_remember_me" />
	<property name="tokenValiditySeconds" value="7200" />
	<property name="tokenRepository" ref="inMemoryTokenRepository" />
	<!-- 下面就是我自己实现的UserDetailsService 我给了alias "hibernateUserDetailsService" -->
	<property name="userDetailsService" ref="hibernateUserDetailsService" />
</bean>

<bean id="rememberMeAuthenticationProvider"
	class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
	<property name="key" value="#{securityKeys['remember-me']}" /> <!-- KEY 用于加密 两个一定要相同 -->
</bean>

<bean id="inMemoryTokenRepository" class="org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl" />

<!--  
<bean id="jdbcRememberMeTokenRepository"
	class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
	<property name="dataSource" ref="dataSource" />
</bean>
-->

被我注释掉的jdbcRememberMeTokenRepository需要一个数据库表
这个表用来持久化RememberMeToken,生产环境一般还是要这样做的。
create table persistent_logins (
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null
);


3.9 anonymousProcessingFilter:如果用户不能通过验证则给添加一个匿名用户的角色
<bean id="anonymousProcessingFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
	<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
	<property name="key" value="#{securityKeys['anonymous']}"/>
</bean>


3.10 exceptionTranslationFilter: 验证通不过?没有访问权限?这个Filter决定如果出现异常了到底应该这么办。
<bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
	<property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
	<property name="accessDeniedHandler" ref="accessDeniedHandler" />
</bean>

<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
	<property name="useForward" value="false" />
	<property name="loginFormUrl" value="/common/login" />
</bean>

<bean id="accessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
	<property name="errorPage" value="/common/error"/>
</bean>


3.11 filterSecurityInterceptor:核心过滤器的最后一个。它完成最终的授权判断
下面的配置有点多,那是因为filterSecurityInterceptor是个懒家伙。
它把工作委托AuthenticationManager接口,AuthenticationManager接口也不真的干活,
它委托多个AuthenticationProvider接口,当然其中一个AuthenticationProvider还是要
把工作委托给我们的UserDetailsService实现的。最后投票决定到最终结果。
<bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
	<property name="authenticationManager" ref="customAuthenticationManager" />
	<property name="accessDecisionManager" ref="affirmativeBased" />
	<property name="securityMetadataSource">
		<security:filter-security-metadata-source use-expressions="true">
			<security:intercept-url pattern="/common/login" access="permitAll" />
			<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
		</security:filter-security-metadata-source>
	</property>
</bean>

<bean id="customAuthenticationManager" class="org.springframework.security.authentication.ProviderManager">
	<property name="authenticationEventPublisher" ref="defaultAuthEventPublisher"/>
	<property name="providers">
		<list>
			<ref local="daoAuthenticationProvider"/>
			<ref local="anonymousAuthenticationProvider"/>
			<ref local="rememberMeAuthenticationProvider"/>
		</list>
	</property>
</bean>

<!--
	这个Bean决定了投票策略,decisionVoters只要有任意一个决定通过,那么结果就是通过。
-->
<bean class="org.springframework.security.access.vote.AffirmativeBased" id="affirmativeBased">
	<property name="decisionVoters">
		<list>
			<ref bean="roleVoter"/>
			<ref bean="expressionVoter"/>
			<ref bean="authenticatedVoter"/>
		</list>
	</property>
</bean>

<bean class="org.springframework.security.access.vote.RoleVoter" id="roleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" id="authenticatedVoter" />

<bean id="defaultAuthEventPublisher" class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher"/>

<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
	<property name="passwordEncoder" ref="md5PasswordEncoder"/>
	<property name="userDetailsService" ref="hibernateUserDetailsService" />
	<!-- 
	<property name="saltSource" ref="saltSource"/>
	-->
</bean>

<bean id="anonymousAuthenticationProvider" class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
	<property name="key" value="#{securityKeys['anonymous']}" />
</bean>

<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" id="expressionHandler"/>

<bean class="org.springframework.security.web.access.expression.WebExpressionVoter" id="expressionVoter">
	<property name="expressionHandler" ref="expressionHandler"/>
</bean>


3.12 其他的工具bean
<util:properties id="securityKeys">
	<prop key="remember-me">182301IEKO1L73C181891TLTKABCNKA1956A7G9UPQXN</prop>
	<prop key="anonymous">BF93JFJ091N00Q7HF</prop>
</util:properties>

<alias name="userDao" alias="hibernateUserDetailsService"/>

<bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="md5PasswordEncoder" />

<!-- 如果使用 Sha加密的话,可以用一用 -->
<!--
<bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="saltSource">
	<property name="userPropertyToUse" value="id"/>
</bean>
-->


(四) How to
4.1 在MVC框架里,我该如何得到现在已经登录的用户?
User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();


4.2 登录页面我要做后端验证,我该怎么办?
usernamePasswordProcessingFilter实际完成这个功能,自己写这个类。
public class ValidatedUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {

		Authentication result = null;
		Locale locale = request.getLocale();

		try {
			result = super.attemptAuthentication(request, response);
			logger.debug("登录成功");
		} catch (AuthenticationException failed) {
			logger.debug("登录失败");
			String msg = super.messages.getMessage("validator.login.fail", locale);
			request.setAttribute("fail", msg);
			throw failed;
		}
		return result;
	}
}
// 注意这个类是实现了MessageSourceAware接口的。你加载了国际化文件的MessageSource用就好了。但是一定要配置在 WebApplicationContext里,不要配置在springMVC特有的配置文件里。
分享到:
评论
10 楼 wellbeing_wang 2013-12-22  
你好:
   这个相关源码能否提供一份?谢谢!wangwenbincn@163.com
9 楼 guang.027 2013-12-11  
能否给相关源,谢谢,flash8627@hotmail.com
8 楼 leon.s.kennedy 2012-05-10  
yingzhor 写道
我就是用的这个呀。

<sec:authorize access="hasRole('ROLE_ADMIN')">
这段文本只有管理员可见
</sec:authorize>

如果登录的用户有管理员的角色的话,中间的文本他就可以看见了。


那您就把ROLE_ADMIN角色写死在jsp中
ROLE_ADMIN角色删除不了
如果其他角色想访问该资源,还得改源码
小弟糊涂,还望大·侠指点!
方便的话加我qq459109544
谢谢
7 楼 yingzhor 2012-05-10  
我就是用的这个呀。

<sec:authorize access="hasRole('ROLE_ADMIN')">
这段文本只有管理员可见
</sec:authorize>

如果登录的用户有管理员的角色的话,中间的文本他就可以看见了。
6 楼 leon.s.kennedy 2012-05-10  
yingzhor 写道
leon.s.kennedy 写道
yingzhor 写道
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。


请问<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />
PERMISSION_CAN_DO_SOMETHING是表达什么意思?写死在这里吗?

页面标签<sec:authorize url="/add*">不好使,看官方文档说在应用程序上下文必须有webapplicationContext实例,才能用 google上查不到 请帮忙解答,非常感谢!


你好。
"PERMISSION_CAN_DO_SOMETHING"就是"GrantedAuthority"接口的"generateGrantedAuthority()"方法的返回值,我是用Hibernate实现的UserDetailsService从数据库里把这个字符串取出来。

这个字符串就是一个用户有没有某种权限判断的依据。

<sec:authorize url="/add*">我也没有用过。


我现在是把资源url写在DB中,配置文件中不需要
也是自定义的filter
使用ss3提供的JSP标签,<sec:authorize 来控制页面上显示的内容,请问您是怎么做到的?
5 楼 yingzhor 2012-05-10  
leon.s.kennedy 写道
yingzhor 写道
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。


请问<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />
PERMISSION_CAN_DO_SOMETHING是表达什么意思?写死在这里吗?

页面标签<sec:authorize url="/add*">不好使,看官方文档说在应用程序上下文必须有webapplicationContext实例,才能用 google上查不到 请帮忙解答,非常感谢!


你好。
"PERMISSION_CAN_DO_SOMETHING"就是"GrantedAuthority"接口的"generateGrantedAuthority()"方法的返回值,我是用Hibernate实现的UserDetailsService从数据库里把这个字符串取出来。

这个字符串就是一个用户有没有某种权限判断的依据。

<sec:authorize url="/add*">我也没有用过。
4 楼 leon.s.kennedy 2012-05-10  
yingzhor 写道
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。


请问<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />
PERMISSION_CAN_DO_SOMETHING是表达什么意思?写死在这里吗?

页面标签<sec:authorize url="/add*">不好使,看官方文档说在应用程序上下文必须有webapplicationContext实例,才能用 google上查不到 请帮忙解答,非常感谢!
3 楼 yingzhor 2012-05-03  
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。
2 楼 yingzhor 2012-05-03  
我不太了解你所指的“把角色写死在配置文件里”是什么意思。

我并没有这样做,GrantedAuthority的实现类是Permission 而不是 Role。
而Permission也是数据库相关的实体类。也就是说,实际上权限的信息是存放在数据库里的。

因为我的文章只是做个简单的Demo, 我其实偷换了一下概念,让Role和Permission都具有了
GrantedAuthority定义的语义。在生产上我们不会这么做。

引用

前台页面标签中,如何控制显示连接?也把角色写死吗?

SpringSecurity提供了JSP标签,用来决定显示/隐藏页面的内容。

<security:authorize /> 具体使用方法请查看官方文档。

引用

还用种匹配url的标签,您不妨试试看

谢谢,我知道Spring Security还特别提供了一个security的xml schema,
但是个人觉得使用那样的配置方式虽然简单,但是隐藏的细节太多。

有时候想改变一下spring security框架的默认行为就不是那么方便了。
1 楼 leon.s.kennedy 2012-05-03  
博主您好
您如此配置ss3文件,把角色写死在配置文件里,如果今后角色是可以维护的怎办?
前台页面标签中,如何控制显示连接?也把角色写死吗?
还用种匹配url的标签,您不妨试试看

相关推荐

    Spring Security in Action

    Spring Security 实践指南 Spring Security 是一个基于 Java 的安全框架,旨在提供身份验证、授权和访问控制等功能。下面是 Spring Security 的主要知识点: 一、身份验证(Authentication) 身份验证是指对用户...

    Spring Security 资料合集

    这三份资料——"实战Spring Security 3.x.pdf"、"Spring Security 3.pdf" 和 "Spring Security使用手册.pdf" 将深入探讨这些概念,并提供实践指导,帮助读者掌握如何在实际项目中应用Spring Security。通过学习这些...

    springsecurity学习笔记

    在"springsecurity学习笔记"中,你可能会涉及以下主题: - Spring Security的基本配置,包括web安全配置和全局安全配置。 - 如何自定义认证和授权流程,比如实现自定义的AuthenticationProvider和...

    Spring Security3.1实践

    《Spring Security 3.1 实践》 Spring Security 是一个强大的、高度可定制的身份验证和访问控制框架,广泛应用于Java企业级应用中。在Spring Security 3.1版本中,它提供了一套全面的安全解决方案,涵盖了从用户...

    spring security 完整项目实例

    Spring Security 是一个强大的安全框架,用于为Java应用提供身份验证和授权服务。在这个完整的项目实例中...通过学习和实践这个项目,开发者能够深入理解Spring Security的工作原理,从而更有效地保护自己的Java应用。

    Spring Cloud Gateway 整合 Spring Security 统一登录认证鉴权

    在压缩包文件`spring_gateway_security_webflux`中,可能包含了示例代码或配置文件,用于演示如何在Spring Cloud Gateway中集成Spring Security,实现统一登录认证鉴权。这些资源可以帮助开发者更快地理解和实践上述...

    Spring security认证授权

    Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于Java应用程序。它提供了全面的安全解决方案,包括用户认证、权限授权、会话管理、CSRF防护以及基于HTTP的访问控制。在这个例子中,我们将...

    spring spring security2.5 jar

    Spring Security 2.5虽然已经较为老旧,但其设计理念和核心机制仍然适用于现代的开发实践。理解并熟练掌握这个版本,有助于深入理解Spring Security的工作原理,并为升级到更高版本或使用其他安全框架打下坚实基础。

    SpringSecurity素材.rar

    SpringSecurity是Java领域中一款强大的安全框架,专为Spring和Spring Boot应用设计,提供全面的安全管理解决方案。...学习SpringSecurity时,理解其核心原理并结合实际项目进行实践,是掌握这个框架的关键。

    SpringBoot+SpringSecurity+WebSocket

    在IT行业中,SpringBoot、SpringSecurity和WebSocket是三个非常重要的技术组件,它们分别在应用程序开发、安全管理和实时通信方面发挥着关键作用。本项目结合这三个技术,构建了一个整合的示例,旨在展示如何在...

    SpringSecurity中文文档.zip

    SpringSecurity是Java开发领域中广泛使用的安全框架,用于保护Web应用程序免受各种安全威胁。它提供了全面的身份验证、授权和访问控制功能。本压缩包包含了两份重要的SpringSecurity中文文档,分别为“Spring ...

    SpringSecurity源码

    本教程配套源码旨在帮助开发者深入理解SpringSecurity的工作原理,通过实践提升在安全领域的技能。 在SpringSecurity中,核心概念主要包括以下几个部分: 1. **过滤器链**:SpringSecurity的核心是基于过滤器的Web...

    Spring security oauth源码

    Spring Security OAuth 是一个用于保护RESTful Web服务的框架,它为OAuth 1.0a和OAuth 2.0协议提供了全面的支持。在这个源码中,我们可能会看到如何将Spring Security与OAuth结合,以构建安全的Web应用程序和服务。...

    spring_security_3.1

    Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,专为Java应用程序设计。它在Spring生态系统中...通过阅读提供的`spring_security_3.1.pdf`文档,你可以更深入地学习这个框架的细节和实践应用。

    spring security 3 demos

    通过对这些示例代码的学习和实践,开发者可以深入理解Spring Security 3的核心概念,并将其应用于实际项目中,确保应用程序的安全性。记得查看附带的使用文档,这将有助于理解和配置每个示例。同时,配合jar包,你...

    Spring Security-3中文官方文档(及教程)

    6. **记住我功能**:Spring Security支持“记住我”功能,允许用户在一段时间内无须重新登录。这通过在客户端存储一个长期有效的令牌来实现。 7. **OAuth2整合**:Spring Security可以与OAuth2框架集成,支持第三方...

    springSecurityTest.zip

    总结起来,"springSecurityTest.zip"文件提供了一个完整的环境,让你可以动手实践Spring Security的基础用法。通过学习其中的代码、笔记和文档,你将理解Spring Security如何与Spring框架协同工作,如何设置认证和...

    IDEA+SpringBoot+SpringSecurity:整个SpringSecurity的实现过程,可应用于线上产品

    SpringSecurity不仅提供了基础的安全功能,还考虑了安全性最佳实践,如防止XSS(跨站脚本攻击)、CSRF、点击劫持等。此外,它还支持SSL/TLS加密,确保数据传输的安全性。 在部署和监控方面,SpringSecurity的日志...

    SpringMVC集成SpringSecurity

    在“SpringMVC 集成 SpringSecurity”的实践中,我们需要理解以下几个关键知识点: 1. **SpringSecurity架构**:SpringSecurity的核心架构由多个组件构成,包括过滤器链、认证管理、权限控制等。其中,过滤器链是...

Global site tag (gtag.js) - Google Analytics