如何使用spring security,相信百度过的都知道,总共有四种用法,从简到深为:一、不用数据库,所有数据写在配置文件,这个也是官方文档里面的demo;二、使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,并且那个数据库设计得很简陋,实用性差;三、spring security和Acegi不一样,它不能修改默认filter了,但支持插入filter,因此根据这个,咱们能够插入本身的filter来灵活使用;四、暴力手段,修改源码,前面说的修改默认filter只是修改配置文件以替换filter而已,这种是直接改了里面的源码,可是这种不符合OO设计原则,并且不实际,不可用。css
由于本文准备介绍第三种方法,因此面向的读者是已经具有了spring security基础知识的。不过没关系,读者能够先看一下这个教程,看完应该可使用第二种方法开发了。html
使用众多的拦截器对url拦截,以此来管理权限。可是这么多拦截器,笔者不可能对其一一来说,主要讲里面核心流程的两个。 首先,权限管理离不开登录验证的,因此登录验证拦截器AuthenticationProcessingFilter要讲; 还有就是对访问的资源管理吧,因此资源管理拦截器AbstractSecurityInterceptor要讲; 但拦截器里面的实现须要一些组件来实现,因此就有了AuthenticationManager、accessDecisionManager等组件来支撑。 如今先大概过一遍整个流程,用户登录,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,并且AuthenticationManager会调用ProviderManager来获取用户验证信息(不一样的Provider调用的服务不一样,由于这些信息能够是在数据库上,能够是在LDAP服务器上,能够是xml配置文件上等),若是验证经过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。 访问资源(即受权管理),访问url时,会经过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的所有权限,在调用受权管理器AccessDecisionManager,这个受权管理器会经过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的所有权限,而后根据所配的策略(有:一票决定,一票否认,少数服从多数等),若是权限足够,则返回,权限不够则报错并调用权限不足页面。 虽然讲得好像好复杂,读者们可能有点晕,不过不打紧,真正经过代码的讲解在后面,读者能够看完后面的代码实现,再返回看这个简单的原理,可能会有不错的收获。 java
javaEE的入口:web.xml:web
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<!--?xml version=
"1.0"
encoding=
"UTF-8"
?-->
<web-app version=
"2.5"
xmlns=
"http://java.sun.com/xml/ns/javaee"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
>
<!--加载Spring XML配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value> classpath:securityConfig.xml </param-value>
</context-param>
<!-- Spring Secutiry3.
1
的过滤器链配置 -->
<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>
<!-- Spring 容器启动监听器 -->
<listener>
<listener-
class
>org.springframework.web.context.ContextLoaderListener</listener-
class
>
</listener>
<!--系统欢迎页面 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
|
上面那个配置不用多说了吧 直接上spring security的配置文件securityConfig.xml:正则表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
<!--?xml version=
"1.0"
encoding=
"UTF-8"
?-->
<b:beans xmlns=
"http://www.springframework.org/schema/security"
xmlns:b=
"http://www.springframework.org/schema/beans"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-
3.0
.xsd
http:
//www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!--登陆页面不过滤 -->
<http pattern=
"/login.jsp"
security=
"none"
>
<http access-denied-page=
"/accessDenied.jsp"
>
<form-login login-page=
"/login.jsp"
>
<!--访问/http:
//blog.csdn.net/u012367513/article/details/admin.jsp资源的用户必须具备ROLE_ADMIN的权限 -->
<!-- <intercept-url pattern=
"/http://blog.csdn.net/u012367513/article/details/admin.jsp"
access=
"ROLE_ADMIN"
/> -->
<!--访问/**资源的用户必须具备ROLE_USER的权限 -->
<!-- <intercept-url pattern=
"/**"
access=
"ROLE_USER"
/> -->
<session-management>
<concurrency-control max-sessions=
"1"
error-
if
-maximum-exceeded=
"false"
>
</concurrency-control></session-management>
<!--增长一个filter,这点与 Acegi是不同的,不能修改默认的filter了, 这个filter位于FILTER_SECURITY_INTERCEPTOR以前 -->
<custom-filter ref=
"myFilter"
before=
"FILTER_SECURITY_INTERCEPTOR"
>
</custom-filter></form-login></http>
<!--一个自定义的filter,必须包含 authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
咱们的全部控制将在这三个类中实现,解释详见具体配置 -->
<b:bean id=
"myFilter"
class
=
"com.erdangjiade.spring.security.MyFilterSecurityInterceptor"
>
<b:property name=
"authenticationManager"
ref=
"authenticationManager"
>
<b:property name=
"accessDecisionManager"
ref=
"myAccessDecisionManagerBean"
>
<b:property name=
"securityMetadataSource"
ref=
"securityMetadataSource"
>
</b:property></b:property></b:property></b:bean>
<!--验证配置,认证管理器,实现用户认证的入口,主要实现UserDetailsService接口便可 -->
<!--若是用户的密码采用加密的话 <password-encoder hash=
"md5"
/> -->
</authentication-provider>
</authentication-manager>
<!--在这个类中,你就能够从数据库中读入用户的密码,角色信息,是否锁定,帐号是否过时等 -->
<b:bean id=
"myUserDetailService"
class
=
"com.erdangjiade.spring.security.MyUserDetailService"
>
<!--访问决策器,决定某个用户具备的角色,是否有足够的权限去访问某个资源 -->
<b:bean id=
"myAccessDecisionManagerBean"
class
=
"com.erdangjiade.spring.security.MyAccessDecisionManager"
>
</b:bean>
<!--资源源数据定义,将全部的资源和权限对应关系创建起来,即定义某一资源能够被哪些角色访问 -->
<b:bean id=
"securityMetadataSource"
class
=
"com.erdangjiade.spring.security.MyInvocationSecurityMetadataSource"
>
</b:bean></b:bean></http></b:beans>
|
其实全部配置都在里面,首先这个版本的spring security不支持了filter=none的配置了,改为了独立的,里面你能够配登录页面、权限不足的返回页面、注销页面等,上面那些配置,我注销了一些资源和权限的对应关系,笔者这里不须要在这配死它,能够本身写拦截器来得到资源与权限的对应关系。 session-management是用来防止多个用户同时登录一个帐号的。
最重要的是笔者本身写的拦截器myFilter(终于讲到重点了),首先这个拦截器会加载在FILTER_SECURITY_INTERCEPTOR以前(配置文件上有说),最主要的是这个拦截器里面配了三个处理类,第一个是authenticationManager,这个是处理验证的,这里须要特别说明的是:这个类不单只这个拦截器用到,还有验证拦截器AuthenticationProcessingFilter也用到 了,并且实际上的登录验证也是AuthenticationProcessingFilter拦截器调用authenticationManager来处理的,咱们这个拦截器只是为了拿到验证用户信息而已(这里不太清楚,由于authenticationManager笔者设了断点,用户登录后再也没调用这个类了,并且调用这个类时不是笔者本身写的那个拦截器调用的,看了spring技术内幕这本书才知道是AuthenticationProcessingFilter拦截器调用的)。 securityMetadataSource这个用来加载资源与权限的所有对应关系的,并提供一个经过资源获取全部权限的方法。
accessDecisionManager这个也称为受权器,经过登陆用户的权限信息、资源、获取资源所需的权限来根据不一样的受权策略来判断用户是否有权限访问资源。
authenticationManager类能够有许多provider(提供者)提供用户验证信息,这里笔者本身写了一个类myUserDetailService来获取用户信息。
MyUserDetailService:spring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package
com.erdangjiade.spring.security;
import
java.util.ArrayList;
import
java.util.Collection;
import
org.springframework.dao.DataAccessException;
import
org.springframework.security.core.GrantedAuthority;
import
org.springframework.security.core.authority.GrantedAuthorityImpl;
import
org.springframework.security.core.userdetails.User;
import
org.springframework.security.core.userdetails.UserDetails;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.core.userdetails.UsernameNotFoundException;
public
class
MyUserDetailService
implements
UserDetailsService {
//登录验证时,经过username获取用户的全部权限信息,
//并返回User放到spring的全局缓存SecurityContextHolder中,以供受权器使用
public
UserDetails loadUserByUsername(String username)
throws
UsernameNotFoundException, DataAccessException {
Collection<grantedauthority> auths=
new
ArrayList<grantedauthority>();
GrantedAuthorityImpl auth2=
new
GrantedAuthorityImpl(
"ROLE_ADMIN"
);
GrantedAuthorityImpl auth1=
new
GrantedAuthorityImpl(
"ROLE_USER"
);
if
(username.equals(
"lcy"
)){
auths=
new
ArrayList<grantedauthority>();
auths.add(auth1);
auths.add(auth2);
}
User user =
new
User(username,
"lcy"
,
true
,
true
,
true
,
true
, auths);
return
user;
}
} </grantedauthority></grantedauthority></grantedauthority>
|
其中UserDetailsService接口是spring提供的,必须实现的。别看这个类只有一个方法,并且这么简单,其中内涵玄机。 读者看到这里可能就大感疑惑了,不是说好的用数据库吗?对,但别急,等笔者慢慢给大家解析。 首先,笔者为何不用数据库,还不是为了读者们测试方便,并简化spring security的流程,让读者抓住主线,而不是还要烦其余事(导入数据库,配置数据库,写dao等)。 这里笔者只是用几个数据模拟了从数据库中拿到的数据,也就是说ROLE_ADMIN、ROLE_USER、lcy(第一个是登录帐号)、lcy(第二个是密码)是从数据库拿出来的,这个不难实现吧,若是须要数据库时,读者能够用本身写的dao经过参数username来查询出这个用户的权限信息(或是角色信息,就是那个ROLE_*,对必须是ROLE_开头的,否则spring security不认帐的,实际上是spring security里面作了一个判断,必需要ROLE_开头,读者能够百度改一下),再返回spring自带的数据模型User便可。 这个写应该比较清晰、灵活吧,总之数据读者们经过什么方法获取都行,只要返回一个User对象就好了。(这也是笔者为何要重写这个类的缘由)
经过MyUserDetailService拿到用户信息后,authenticationManager对比用户的密码(即验证用户),而后这个AuthenticationProcessingFilter拦截器就过咯。
下面要说的是另一个拦截器,就是笔者本身写的拦截器MyFilterSecurityInterceptor:sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
package
com.erdangjiade.spring.security;
import
java.io.IOException;
import
javax.servlet.Filter;
import
javax.servlet.FilterChain;
import
javax.servlet.FilterConfig;
import
javax.servlet.ServletException;
import
javax.servlet.ServletRequest;
import
javax.servlet.ServletResponse;
import
org.springframework.security.access.SecurityMetadataSource;
import
org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import
org.springframework.security.access.intercept.InterceptorStatusToken;
import
org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
public
class
MyFilterSecurityInterceptor
extends
AbstractSecurityInterceptor
implements
Filter {
//配置文件注入
private
FilterInvocationSecurityMetadataSource securityMetadataSource;
//登录后,每次访问资源都经过这个拦截器拦截
public
void
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws
IOException, ServletException {
FilterInvocation fi =
new
FilterInvocation(request, response, chain);
invoke(fi);
}
public
FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return
this
.securityMetadataSource;
}
public
Class<!--?
extends
Object--> getSecureObjectClass() {
return
FilterInvocation.
class
;
}
public
void
invoke(FilterInvocation fi)
throws
IOException, ServletException {
//fi里面有一个被拦截的url
//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的全部权限
//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
InterceptorStatusToken token =
super
.beforeInvocation(fi);
try
{
//执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally
{
super
.afterInvocation(token,
null
);
}
}
public
SecurityMetadataSource obtainSecurityMetadataSource() {
return
this
.securityMetadataSource;
}
public
void
setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource newSource)
{
this
.securityMetadataSource = newSource;
}
public
void
destroy() {
}
public
void
init(FilterConfig arg0)
throws
ServletException {
}
}
|
继承AbstractSecurityInterceptor、实现Filter是必须的。 首先,登录后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,这个方法调用了invoke方法,其中fi断点显示是一个url(可能重写了toString方法吧,可是里面还有一些方法的),最重要的是beforeInvocation这个方法,它首先会调用MyInvocationSecurityMetadataSource类的getAttributes方法获取被拦截url所需的权限,在调用MyAccessDecisionManager类decide方法判断用户是否够权限。弄完这一切就会执行下一个拦截器。
再看一下这个MyInvocationSecurityMetadataSource的实现:数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package
com.erdangjiade.spring.security;
import
java.util.ArrayList;
import
java.util.Collection;
import
java.util.HashMap;
import
java.util.Iterator;
import
java.util.Map;
import
org.springframework.security.access.ConfigAttribute;
import
org.springframework.security.access.SecurityConfig;
import
org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import
com.erdangjiade.spring.security.tool.AntUrlPathMatcher;
import
com.erdangjiade.spring.security.tool.UrlMatcher;
public
class
MyInvocationSecurityMetadataSource
implements
FilterInvocationSecurityMetadataSource {
private
UrlMatcher urlMatcher =
new
AntUrlPathMatcher();
private
static
Map<string, collection<configattribute=
""
>> resourceMap =
null
;
//tomcat启动时实例化一次
public
MyInvocationSecurityMetadataSource() {
loadResourceDefine();
}
//tomcat开启时加载一次,加载全部url和权限(或角色)的对应关系
private
void
loadResourceDefine() {
resourceMap =
new
HashMap<string, collection<configattribute=
""
>>();
Collection<configattribute> atts =
new
ArrayList<configattribute>();
ConfigAttribute ca =
new
SecurityConfig(
"ROLE_USER"
);
atts.add(ca);
resourceMap.put(
"/index.jsp"
, atts);
Collection<configattribute> attsno =
new
ArrayList<configattribute>();
ConfigAttribute cano =
new
SecurityConfig(
"ROLE_NO"
);
attsno.add(cano);
}
//参数是要访问的url,返回这个url对于的全部权限(或角色)
public
Collection<configattribute> getAttributes(Object object)
throws
IllegalArgumentException {
// 将参数转为url
String url = ((FilterInvocation)object).getRequestUrl();
Iterator<string>ite = resourceMap.keySet().iterator();
while
(ite.hasNext()) {
String resURL = ite.next();
if
(urlMatcher.pathMatchesUrl(resURL, url)) {
return
resourceMap.get(resURL);
}
}
return
null
;
}
public
boolean
supports(Class<!--?-->clazz) {
return
true
;
}
public
Collection<configattribute> getAllConfigAttributes() {
return
null
;
}
}
</configattribute></string></configattribute></configattribute></configattribute></configattribute></configattribute></string,></string,>
|
实现FilterInvocationSecurityMetadataSource接口也是必须的。 首先,这里也是模拟了从数据库中获取信息。 其中loadResourceDefine方法不是必须的,这个只是加载全部的资源与权限的对应关系并缓存起来,避免每次获取权限都访问数据库(提升性能),而后getAttributes根据参数(被拦截url)返回权限集合。 这种缓存的实现其实有一个缺点,由于loadResourceDefine方法是放在构造器上调用的,而这个类的实例化只在web服务器启动时调用一次,那就是说loadResourceDefine方法只会调用一次,若是资源和权限的对应关系在启动后发生了改变,那么缓存起来的就是脏数据,而笔者这里使用的就是缓存数据,那就会受权错误了。但若是资源和权限对应关系是不会改变的,这种方法性能会好不少。 如今说回有数据库的灵活实现,读者看到这,可能会说,这还不简单,和上面MyUserDetailService类同样使用dao灵活获取数据就行啦。 若是读者这样想,那只想到了一半,想一下spring的机制(依赖注入),dao须要依赖注入吧,但这是在启动时候,那个dao可能都还没加载,因此这里须要读者本身写sessionFactory,本身写hql或sql,对,就在loadResourceDefine方法里面写(这个应该会写吧,基础来的)。那若是说想用第二种方法呢(就是容许资源和权限的对应关系改变的那个),那更加简单,根本不须要loadResourceDefine方法了,直接在getAttributes方法里面调用dao(这个是加载完,后来才会调用的,因此可使用dao),经过被拦截url获取数据库中的全部权限,封装成Collection返回就好了。(灵活、简单) 注意:接口UrlMatcher和实现类AntUrlPathMatcher是笔者本身写的,这原本是spring之前版本有的,如今没有了,可是以为好用就用会来了,直接上代码(读者也能够本身写正则表达式验证被拦截url和缓存或数据库的url是否匹配):缓存
1
2
3
4
5
6
7
8
|
package
com.erdangjiade.spring.security.tool;
public
interface
UrlMatcher{
Object compile(String paramString);
boolean
pathMatchesUrl(Object paramObject, String paramString);
String getUniversalMatchPattern();
boolean
requiresLowerCaseUrl();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package
com.erdangjiade.spring.security.tool;
import
org.springframework.util.AntPathMatcher;
import
org.springframework.util.PathMatcher;
public
class
AntUrlPathMatcher
implements
UrlMatcher {
private
boolean
requiresLowerCaseUrl;
private
PathMatcher pathMatcher;
public
AntUrlPathMatcher() {
this
(
true
);
}
public
AntUrlPathMatcher(
boolean
requiresLowerCaseUrl)
{
this
.requiresLowerCaseUrl =
true
;
this
.pathMatcher =
new
AntPathMatcher();
this
.requiresLowerCaseUrl = requiresLowerCaseUrl;
}
public
Object compile(String path) {
if
(
this
.requiresLowerCaseUrl) {
return
path.toLowerCase();
}
return
path;
}
public
void
setRequiresLowerCaseUrl(
boolean
requiresLowerCaseUrl){
this
.requiresLowerCaseUrl = requiresLowerCaseUrl;
}
public
boolean
pathMatchesUrl(Object path, String url) {
if
((
"/**"
.equals(path)) || (
"**"
.equals(path))) {
return
true
;
}
return
this
.pathMatcher.match((String)path, url);
}
public
String getUniversalMatchPattern() {
return
"/**"
;
}
public
boolean
requiresLowerCaseUrl() {
return
this
.requiresLowerCaseUrl;
}
public
String toString() {
return
super
.getClass().getName() +
"[requiresLowerCase='"
+
this
.requiresLowerCaseUrl +
"']"
;
}
}
|
而后MyAccessDecisionManager类的实现:tomcat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package
com.erdangjiade.spring.security;
import
java.util.Collection;
import
java.util.Iterator;
import
org.springframework.security.access.AccessDecisionManager;
import
org.springframework.security.access.AccessDeniedException;
import
org.springframework.security.access.ConfigAttribute;
import
org.springframework.security.access.SecurityConfig;
import
org.springframework.security.authentication.InsufficientAuthenticationException;
import
org.springframework.security.core.Authentication;
import
org.springframework.security.core.GrantedAuthority;
public
class
MyAccessDecisionManager
implements
AccessDecisionManager {
//检查用户是否够权限访问资源
//参数authentication是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息
//参数object是url
//参数configAttributes所需的权限
public
void
decide(Authentication authentication, Object object,
Collection<configattribute> configAttributes)
throws
AccessDeniedException, InsufficientAuthenticationException {
if
(configAttributes ==
null
){
return
;
}
Iterator<configattribute> ite=configAttributes.iterator();
while
(ite.hasNext()){
ConfigAttribute ca=ite.next();
String needRole=((SecurityConfig)ca).getAttribute();
for
(GrantedAuthority ga : authentication.getAuthorities()){
if
(needRole.equals(ga.getAuthority())){
return
;
}
}
}
//注意:执行这里,后台是会抛异常的,可是界面会跳转到所配的access-denied-page页面
throw
new
AccessDeniedException(
"no right"
);
}
public
boolean
supports(ConfigAttribute attribute) {
return
true
;
}
public
boolean
supports(Class<!--?-->clazz) {
return
true
;
}
}</configattribute></configattribute>
|
接口AccessDecisionManager也是必须实现的。 decide方法里面写的就是受权策略了,笔者的实现是,没有明说须要权限的(即没有对应的权限的资源),能够访问,用户具备其中一个或多个以上的权限的能够访问。这个就看需求了,须要什么策略,读者能够本身写其中的策略逻辑。经过就返回,不经过抛异常就好了,spring security会自动跳到权限不足页面(配置文件上配的)。
就这样,整个流程过了一遍。
原本想给这个demo的源码出来的,可是笔者以为,经过这个教程一步一步读下来,并本身敲一遍代码,会比直接运行一遍demo印象更深入,而且更容易理解里面的原理。 并且个人源码其实都公布出来了: login.jsp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<%
@page
language=
"java"
import
=
"java.util.*"
pageEncoding=
"UTF-8"
%>
<title>登陆</title>
<form action=
"j_spring_security_check"
method=
"POST"
>
<table>
<tbody><tr>
<td>用户:</td>
<td><input type=
"'text'"
name=
"'j_username'"
></td>
</tr>
<tr>
<td>密码:</td>
<td><input type=
"'password'"
name=
"'j_password'"
></td>
</tr>
<tr>
<td><input name=
"reset"
type=
"reset"
></td>
<td><input name=
"submit"
type=
"submit"
></td>
</tr>
</tbody></table>
</form>
|
index.jsp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<%
@page
language=
"java"
import
=
"java.util.*"
pageEncoding=
"UTF-8"
%>
<title>My JSP
'index.jsp'
starting page</title>
<h3>这是首页</h3>欢迎
<sec:authentication property=
"name"
> !
<br>
进入admin页面
进入其它页面
</sec:authentication>
|
http://blog.csdn.net/u012367513/article/details/admin.jsp:
1
2
3
4
5
6
7
8
9
|
<%
@page
language=
"java"
import
=
"java.util.*"
pageEncoding=
"utf-8"
%>
欢迎来到管理员页面.
<br>
|
accessDenied.jsp:
1
2
3
4
5
6
7
8
9
|
<%
@page
language=
"java"
import
=
"java.util.*"
pageEncoding=
"utf-8"
%>
欢迎来到管理员页面.
<br>
|
http://blog.csdn.net/u012367513/article/details/other.jsp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<%@ page language=
"java"
import
=
"java.util.*"
pageEncoding=
"UTF-8"
%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+
"://"
+request.getServerName()+
":"
+request.getServerPort()+path+
"/"
;
%>
<base href=
"<%=basePath%>"
>
<meta http-equiv=
"pragma"
content=
"no-cache"
>
<meta http-equiv=
"cache-control"
content=
"no-cache"
>
<meta http-equiv=
"expires"
content=
"0"
>
<meta http-equiv=
"keywords"
content=
"keyword1,keyword2,keyword3"
>
<meta http-equiv=
"description"
content=
"This is my page"
>
<!--
<link rel=
"stylesheet"
type=
"text/css"
href=
"styles.css"
>
-->
<h3>这里是Other页面</h3>
|
项目图:
<img src="http://www.2cto.com/uploadfile/Collfiles/20140829/20140829091240286.png" alt="n峨n竩�漽j喎�" http:="" www.2cto.com="" ym"="" target="_blank" class="keylink" style="border-width: 0px; padding: 0px; margin: 0px; list-style: none; width: 322px; height: 464px;">源码和jar包都在这个教程里面,为何不直接给?笔者的目的是让读者跟着教程敲一遍代码,使印象深入(相信作这行的都知道,一样一段代码,看过和敲过的区别是多么的大),因此不惜如此来强迫你们了。
转自:http://blog.csdn.net/u013516966/article/details/46688765