Skip to content

Commit

Permalink
feat(scaffold): support jwt authentication with shiro
Browse files Browse the repository at this point in the history
  • Loading branch information
taccisum committed Sep 19, 2019
1 parent 17d81d4 commit 7141887
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 31 deletions.
2 changes: 1 addition & 1 deletion generators/app/handler/configurer/configurers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ files.forEach(file => {
}
})

const types = ['discovery', 'db', 'orm', 'dbPool', 'openfeign', 'mq', 'configservice'];
const types = ['discovery', 'db', 'orm', 'dbPool', 'openfeign', 'mq', 'configservice', 'authentication'];

configurers.receive = (event, args) => {
types.forEach(type => {
Expand Down
49 changes: 49 additions & 0 deletions generators/app/handler/configurer/jwt_configurer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const _ = require('lodash');

module.exports = {
key: 'jwt',
fn: {
configureProviderPomDependencies (optionalDependencies, props) {
optionalDependencies.push({
dependency: [
{ groupId: 'com.auth0' },
{ artifactId: 'java-jwt' },
{ version: '3.8.1' }
]
})
if (props.security === 'shiro') {
optionalDependencies.push({
dependency: [
{ groupId: 'com.github.taccisum' },
{ artifactId: 'shiro-starter' },
{ version: '2.1.0' }
]
})
}
},
configureApplicationYaml (yaml, env, props) {
if (props.security === 'shiro') {
switch (env) {
case 'default': {
_.merge(yaml, {
shiro: {
web: {
mode: 'stateless',
'filter-chain-definition': {
authc: [
'/v1/**'
],
anon: [
'/**'
]
}
}
}
});
break;
}
}
}
}
}
}
72 changes: 43 additions & 29 deletions generators/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,35 +120,44 @@ const obj = {
},
option: { desc: '配置中心', type: String, default: 'none' }
},
// authentication: {
// prompting: {
// type: 'list',
// choices: [
// 'token',
// // 'session',
// 'none'
// ],
// message: '请选择你采用的认证机制类型'
// },
// option: { desc: '认证机制', type: String, default: 'token' },
// child: {
// token: {
// prompting: {
// type: 'list',
// choices: [
// 'jwt'
// ],
// message: '请选择你使用的token类型'
// },
// option: { desc: 'token类型', type: String, default: 'jwt' },
// callbacks: {
// trigger (answers) {
// return answers.authentication === 'token';
// }
// }
// }
// }
// },
authentication: {
prompting: {
type: 'list',
choices: [
'jwt',
'none'
],
message: '请选择你采用的认证机制类型'
},
option: { desc: '认证机制', type: String, default: 'jwt' },
child: {
jwtIssue: {
prompting: { type: 'input', default: 'deepexi', message: '请填写你的jwt issue' },
option: { desc: 'jwt issue', type: String, default: 'deepexi' },
callbacks: {
trigger (answers) {
return answers.authentication === 'jwt';
}
}
},
security: {
prompting: {
type: 'list',
choices: [
'shiro'
// 'spring-security'
],
message: '请选择你使用认证框架类型'
},
option: { desc: '认证框架', type: String, default: 'shiro' },
callbacks: {
trigger (answers) {
return answers.authentication !== 'none';
}
}
}
}
},
demo: {
prompting: {
type: 'confirm',
Expand All @@ -160,6 +169,7 @@ const obj = {
}

module.exports = require('yo-power-generator').getGenerator(obj, {
description: 'deepexi spring cloud scaffold',
handlerDir: path.join(__dirname, 'handler'),
templateDir: path.join(__dirname, 'templates'),
afterPropsSet (props) {
Expand Down Expand Up @@ -189,6 +199,10 @@ module.exports = require('yo-power-generator').getGenerator(obj, {
props.conditions[props.configservice] = true;
}

if (props.security !== 'none') {
props.conditions[props.security] = true;
}

props.openfeign = props.discovery === 'eureka';
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ${basePackage}.config;

<%
switch (authentication) {
case 'jwt':
print(`
import com.github.taccisum.shiro.web.autoconfigure.stateless.support.extractor.AuthorizationTokenExtractor;
import com.github.taccisum.shiro.web.autoconfigure.stateless.support.extractor.TokenExtractor;
import com.github.taccisum.shiro.web.autoconfigure.stateless.support.jwt.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.realm.Realm;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class ShiroConfiguration {
@Bean
public Realm realm(JWTManager jwtManager) {
return new OnlyParseJWTRealm("${jwtIssue}", jwtManager);
}

@Bean
public PayloadTemplate payloadTemplate() {
PayloadTemplate payloadTemplate = new DefaultPayloadTemplate("${jwtIssue}");
payloadTemplate.addField("userId", String.class);
payloadTemplate.addField("accountId", String.class);
payloadTemplate.addField("tenantId", String.class);
return payloadTemplate;
}

@Bean
public TokenExtractor tokenExtractor() {
return new AuthorizationTokenExtractor();
}
}
`)
break;

default:
break;
}
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ${basePackage}.util;

<%
switch (authentication) {
case 'jwt':
print(`
import com.github.taccisum.shiro.web.autoconfigure.stateless.support.jwt.JWTPrincipal;
import com.github.taccisum.shiro.web.autoconfigure.stateless.support.jwt.Payload;
import org.apache.shiro.SecurityUtils;

public abstract class AuthUtils {
public static String getToken() {
return getPrincipal().getToken();
}

public static Payload getPayload() {
return getPrincipal().getPayload();
}

private static JWTPrincipal getPrincipal() {
return (JWTPrincipal) SecurityUtils.getSubject().getPrincipal();
}
}
`)
break;

default:
break;
}
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<%
if(authentication !== 'none'){
print(`
swagger.auth.api-key.token.keyName=Authorization
swagger.auth.api-key.token.passAs=header
swagger.auth.api-key.token.includePaths[0]=/**
`)
}
%>
129 changes: 129 additions & 0 deletions generators/test/app/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,56 @@ const helpers = require('yeoman-test')
const path = require('path')
const fs = require('fs');
const yaml = require('js-yaml');
const _ = require('lodash');

function generate (prompts) {
const def = {
groupId: 'com.deepexi',
artifactId: 'foo-service',
basePackage: 'com.deepexi.foo'
}
_.assignIn(def, prompts);
return helpers
.run(path.join(__dirname, '../../app'))
.withPrompts(def)
.then(() => {
})
}

function assertProviderArtifacts (artifacts) {
assert.fileContent(
artifacts.map(artifact => {
return ['foo-service-provider/pom.xml', new RegExp('<artifactId>' + artifact + '<\\/artifactId>')];
})
)
}

function assertNoProviderArtifact (artifacts) {
assert.noFileContent(
artifacts.map(artifact => {
return ['foo-service-provider/pom.xml', new RegExp('<artifactId>' + artifact + '<\\/artifactId>')];
})
)
}

function assertClasses (classes) {
assert.file(classes.map(clazz => {
return `foo-service-provider/src/main/java/com/deepexi/foo/${clazz}`;
}))
}

function assertNoClasses (classes) {
assert.noFile(classes.map(clazz => {
return `foo-service-provider/src/main/java/com/deepexi/foo/${clazz}`;
}))
}

function readYamlConfigs (env) {
if (env) {
return yaml.safeLoad(fs.readFileSync(`foo-service-provider/src/main/resources/application-${env}.yml`));
}
return yaml.safeLoad(fs.readFileSync('foo-service-provider/src/main/resources/application.yml'));
}

describe('generate app', () => {
before(() => {
Expand Down Expand Up @@ -78,6 +128,7 @@ describe('generate app', () => {
})

it('should exists resources files', () => {
assert.file('foo-service-provider/src/main/resources/application.properties')
assert.file('foo-service-provider/src/main/resources/application.yml')
assert.file('foo-service-provider/src/main/resources/application-local.yml')
assert.file('foo-service-provider/src/main/resources/application-dev.yml')
Expand Down Expand Up @@ -446,4 +497,82 @@ describe('optional dependencies', () => {
it('should exist demo files', () => {
});
});

describe('authentication', () => {
const expects = {
jwtAndShiro: {
artifact: {
provider: [
'shiro-starter',
'java-jwt'
]
},
classes: [
'config/ShiroConfiguration.java',
'util/AuthUtils.java'
]
}
}
describe('jwt & shiro', () => {
const expect = expects.jwtAndShiro;

before(() => {
return generate({
authentication: 'jwt',
security: 'shiro',
demo: true
})
});

it('should have dependency', () => {
assertProviderArtifacts(expect.artifact.provider);
});

it('should have properties', () => {
const yaml = readYamlConfigs();
assert(yaml.shiro);
assert.strictEqual(yaml.shiro.web.mode, 'stateless');
});

it('should exist classes', () => {
assertClasses(expect.classes);
});

it('should exist demo files', () => {
});
});

describe('none', () => {
const expect = {
artifact: {
provider: []
},
classes: []
};
for (const key in expects) {
expect.artifact.provider.push(expects[key].artifact.provider);
expect.classes.push(expects[key].classes);
}

before(() => {
return generate({
authentication: 'none',
demo: true
})
});

it('should not have dependency', () => {
assertNoProviderArtifact(expect.artifact.provider);
});

it('should not have properties', () => {
const yaml = readYamlConfigs();
assert(!yaml.shiro);
});

it('should not exist classes', () => {
assertNoClasses(expect.classes);
});
});
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"prettify-xml": "^1.2.0",
"xml": "^1.0.1",
"yeoman-generator": "^1.1.1",
"yo-power-generator": "^0.4.0"
"yo-power-generator": "^0.5.1"
},
"devDependencies": {
"@commitlint/cli": "^8.0.0",
Expand Down

0 comments on commit 7141887

Please sign in to comment.